Day08.mybatis注解开发 您所在的位置:网站首页 mybatis多对一配置 Day08.mybatis注解开发

Day08.mybatis注解开发

2023-03-10 11:08| 来源: 网络整理| 查看: 265

day08-mybatis高级查询、注解开发 第一章 mybatis高级查询【掌握】 1、准备工作 【1】包结构

创建java项目,导入jar包和log4j日志配置文件以及连接数据库的配置文件;

image-20200607074620159

【2】导入SQL脚本

运行资料中的sql脚本:mybatis.sql

1573825517133

1573825721583

【3】创建实体来包,导入资料中的pojo

1573825767426

【4】UserMapper接口 package com.itheima.sh.dao; import com.itheima.sh.pojo.User; public interface UserMapper { //完成根据id查询用户数据; User selectById(Long id); } 【5】UserMapper.xml SELECT * FROM tb_user WHERE id=#{id} 【6】测试 package com.itheima.sh.test; import com.itheima.sh.dao.UserMapper; import com.itheima.sh.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MybatisTest01 { private static UserMapper mapper = null; @BeforeClass public static void beforeClass() throws Exception { //1.构建SessionFactory String resouce = "mybatis-config.xml"; InputStream is = Resources.getResourceAsStream(resouce); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is); //2.获取session SqlSession sqlSession = build.openSession(true); //3.获取接口对象 mapper = sqlSession.getMapper(UserMapper.class); } @Test public void selectById() { User user = mapper.selectById(1L); System.out.println(user); } } 2、表介绍和表关系说明

导入资料中mybatis.sql脚本。新建以下4张表

tb_user:用户表 tb_order:订单表 tb_item:商品表 tb_orderdetail:订单详情表

image-20200607080109587

【表关系】

1.tb_user和 tb_order表关系 tb_user 《==》 tb_order:一对多, 一个人可以下多个订单 tb_order 《==》 tb_user:一对一,一个订单只能属于一个人 结论:tb_user和tb_order属于一对多的关系,需要将一方tb_user的主键作为多方tb_order的外键维护关系 2.tb_order 和 tb_item 表关系 tb_order 《==》 tb_item :一个订单可以有多个商品 tb_item 《==》 tb_order:一个商品可以在多个订单上 结论:tb_order和tb_item属于多对多的关系,需要创建中间表tb_orderdetail维护两个表的关系,并且将两张表 的主键作为中间表的外键 3、一对一查询

需求:通过订单编号20140921003查询出订单信息,并查询出下单人信息。

【实现:关联查询】

【目标】使用多表关联查询,完成根据订单号查询订单信息和下单人信息(订单号:20140921003)

【分析】

一个订单编号对应一个订单,一个订单只能属于一个人。所以上述需求实现是一对一的实现。

【步骤】

1、首先,编写接口方法。编写SQL语句; 2、第二步:分析SQL,封装数据(关联对象); 3、处理多表之间的数据封装(数据库字段名---》实体类的属性名之间的映射)

【实现】

第一步:需求分析

​ 编写多表关联查询SQL,根据订单号查询订单信息及下单人信息;

查询语句以及查询结果:

image-20200607095113017

#方式一:分步查询 #第一步:根据order_number查询订单信息; SELECT * FROM tb_order WHERE order_number = '20140921003'; #第二步:根据订单信息中的user_id查询出下单人的信息; SELECT * FROM tb_user WHERE id = 1; #方式二:多表关联查询,内连接 SELECT * FROM tb_order tbo inner join tb_user tbu on tbo.user_id = tbu.id where tbo.order_number='20140921003' #多表数据封装问题: #关联对象封装数据(在Order中引用User) 第二步:添加关联

修改Order:

​ 在Order类中,添加关联对象User,并添加getter和setter方法;

package com.itheima.sh.pojo; /** * 订单表 * */ public class Order { private Integer id; private String orderNumber; //关联User对象 private User user; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getOrderNumber() { return orderNumber; } public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "Order{" + "id=" + id + ", orderNumber='" + orderNumber + '\'' + ", user=" + user + '}'; } } 第三步:添加方法

编写OrderMapper接口

1551364844245

public interface OrderMapper { /** * 根据订单号查询订单及下单人的信息:方式二 * @param orderNumber * @return */ Order queryOrderAndUserByOrderNumber2(@Param("orderNumber")String orderNumber); } 第四步:编写SQL

在OrderMapper.xml中编写对应的SQL,并将OrderMapper.xml加入到mybatis-config.xml全局配置中;1551364967524

image-20200607095938370

【OrderMapper.xml代码;】

说明:

association:配置关联对象(User)的映射关系 属性: property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象 成员变量名 javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型 SELECT * FROM tb_order tbo INNER JOIN tb_user tbu ON tbo.user_id = tbu.id WHERE tbo.order_number = #{orderNumber}

说明:

1、由于queryOrderAndUserByOrderNumber2查询的结果Order对象中需要封装User信息,所以返回值不能够再使用单纯的resultType来操作; 2、定义resultMap进行关联查询的配置,其中: 属性: id:标识这个resultMap; type:返回的结果类型 autoMapping="true": 表示只需要给当前表的id然后自动映射当前表的其他列值到对应实体类的属性中,这 属于偷懒行为,开发中我们最好都书写出来 子元素: id:主表主键映射 result:主表普通字段的映射 association:关联对象的映射配置 3、association:配置关联对象(User)的映射关系 属性: property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象 成员变量名 javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型 第五步:测试 package com.itheima.sh.test; import com.itheima.sh.dao.OrderMapper; import com.itheima.sh.dao.UserMapper; import com.itheima.sh.pojo.Order; import com.itheima.sh.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.BeforeClass; import org.junit.Test; import java.io.InputStream; public class MybatisTest02 { private static OrderMapper mapper = null; @BeforeClass public static void beforeClass() throws Exception { //1.构建SessionFactory String resouce = "mybatis-config.xml"; InputStream is = Resources.getResourceAsStream(resouce); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is); //2.获取session SqlSession sqlSession = build.openSession(true); //3.获取接口对象 mapper = sqlSession.getMapper(OrderMapper.class); } @Test public void selectById() { Order order = mapper.queryOrderAndUserByOrderNumber2("20140921003"); System.out.println("order = " + order); } }

【测试结果】

image-20200607102841723

注意事项

通过上述测试结果,我们发现User的id是错误的,不是3,正确结果是1:

image-20200607103247424

因为tb_user表的主键是id,tb_order的主键也是id。查询的结果中有两列相同的id字段。在将查询结果封装到实体类的过程中就会封装错误。

注意:user表查询的是id不是id1,由于SQLyog图形化界面显示的原因。可以在cmd窗口查看结果:

image-20200607104119140

【解决方案】

1、建议将所要查询的所有字段显示地写出来; 2、将多表关联查询结果中,相同的字段名取不同的别名;

image-20200607104532901

image-20200607104753543

resultMap中应该如下配置:

image-20200607105105346

【正确结果】

image-20200607105210595

【小结】 一对一关联查询: 1、需要在Order实体类中关联User对象;最终将数据封装到Order中; 2、在OrderMapper.xml文件中书写关联语句并配置关系; 3、关联关系配置: 4、一对多查询

【目标】查询id为1的用户及其订单信息

【分析】

​ 一个用户可以有多个订单。

​ 一个订单只能属于一个用户。

用户(1)-----订单(n)

【步骤】

第一步:查询SQL分析; 第二步:添加关联关系; 第三步:编写接口方法; 第四步:编写映射文件; 第五步:测试

【实现】

第一步:需求分析

编写SQL实现查询id为1的用户及其订单信息

查询语句及查询结果:

image-20200607122112628

#查询id为1的用户及其订单信息 select * from tb_user where id=1; select * from tb_order where user_id=1; #一对多 内连接查询 select * from tb_user tbu inner join tb_order tbo on tbu.id = tbo.user_id where tbu.id=1; # 封装数据:关联对象,一个用户关联多个订单 User(List orderList)

说明:一个用户关联多个订单 User(List orderList) ,在User类中定义一个List集合存储多个订单Order对象。

第二步:添加映射关系

​ 因为一个用户可以拥有多个订单,所以用户和订单是一对多的关系;需要在User类中添加一个List 属性;

package com.itheima.sh.pojo; import java.io.Serializable; import java.util.List; public class User implements Serializable { private Long id; // 用户名 private String userName; // 密码 private String password; // 姓名 private String name; // 年龄 private Integer age; //0 女性 1 男性 private Integer sex; //订单 List orders; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public List getOrders() { return orders; } public void setOrders(List orders) { this.orders = orders; } @Override public String toString() { return "User{" + "id=" + id + ", userName='" + userName + '\'' + ", password='" + password + '\'' + ", name='" + name + '\'' + ", age=" + age + ", sex=" + sex + ", orders=" + orders + '}'; } } 第三步:编写接口方法

在UserMapper接口中,添加关联查询;

/** * 根据用户id查询用户及其订单信息 * @param id * @return */ User oneToManyQuery(@Param("id") Long id); 第四步:编写SQL

​ 在UserMapper.xml文件中编写SQL语句完成一对多的关联查询;

说明:

1.一对多使用collection子标签进行关联多方Order 2.属性: 1)property="orders" 这里的orders表示User类的成员变量orders 2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List 一般不书写 3) ofType="Order" 表示List集合中存储数据的类型 Order 3.一定要记住这里给user表的id起别名是uid,order表的id起别名是oid.在resultMap标签的id子标签中的column属性值书写对应的uid和oid. SELECT tbo.id as oid, tbo.order_number, tbu.id as uid, tbu.user_name, tbu.password, tbu.name, tbu.age, tbu.sex FROM tb_user tbu INNER JOIN tb_order tbo ON tbu.id = tbo.user_id WHERE tbu.id = #{id} 第五步:测试

在用户的测试类中

public class MybatisTest01 { private static UserMapper mapper = null; @BeforeClass public static void beforeClass() throws Exception { //1.构建SessionFactory String resouce = "mybatis-config.xml"; InputStream is = Resources.getResourceAsStream(resouce); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is); //2.获取session SqlSession sqlSession = build.openSession(true); //3.获取接口对象 mapper = sqlSession.getMapper(UserMapper.class); } //根据用户ID查询用户及其订单数据 @Test public void oneToManyQuery() { User user = mapper.oneToManyQuery(1L); System.out.println("user = " + user); } }

image-20200607124912217

【小结】 一对多关系配置: 1、在对象中添加映射关系; 2、编写接口方法,编写SQL; 3、编写resultMap处理数据库字段和实体类之间数据的封装; 5、高级查询小结 resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射 resultMap: 属性: type 结果集对应的数据类型 Order id 唯一标识,被引用的时候,进行指定 autoMapping 开启自动映射 extends 继承 子标签: id:配置id属性 result:配置其他属性 association:配置一对一的映射 property 定义对象的属性名 javaType 属性的类型 autoMapping 开启自动映射 collection:配置一对多的映射 property 定义对象的属性名 javaType 集合的类型 ofType 集合中的元素类型 泛型 autoMapping 开启自动映射 第二章 mybatis注解开发【掌握】 1、概述

​ 上述我们已经学习mybatis的SQL映射文件可以使用xml的方式配置,但是我们发现不同的用户模块接口都对应一个映射文件,并且在映射文件中书写sql语句也比较麻烦。所以Mybatis为用户提供了快速的开发方式,因为有时候大量的XML配置文件的编写时非常繁琐的,因此Mybatis也提供了更加简便的基于注解(Annnotation)的配置方式。

​ 注解配置的方式在很多情况下能够取代mybatis的映射文件,提高开发效率。

2、注解实现CRUD

说明:在演示注解开发之前,为了避免和之前的映射文件混淆,所以可以将之前书写的代码放到一个新的工程中,删除映射文件即可。

2.0、CRUD相关注解

【注解】

@Insert:保存 Value:sql语句(和xml的配置方式一模一样) @Update:更新 Value:sql语句 @Delete: 删除 Value:sql语句 @Select: 查询 Value:sql语句 @Options:可选配置(获取主键) userGeneratedKeys:开关,值为true表示可以获取主键 相当于select last_insert_id() keyProperty :对象属性 keyColumn : 列名

【使用方式】

【第一步】将mybatis全局配置文件mybatis-config.xml中的mapper路径改为包扫描或者class路径;

说明:因为没有了映射文件,所以我们这里采用加载接口方式,需要告知mybatis哪个接口的方法上的注解需要被执行。

image-20200608100720852

【第二步】编写接口和注解;

【第三步】测试

2.1、新增

目标:使用注解@Insert的方式新增数据

步骤:

第一步:UserMapper接口中新增用户方法上面编写注解; 第二步:测试

实现:

第一步:在UserMapper接口中的saveUser()方法上面添加@Insert注解,并设置该注解的value属性值为具体的SQL语句;

public interface UserMapper { //1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量 @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})") void saveUser(User user); }

第二步:测试

在UserMapperTest类下面对save方法进行测试:

public class MybatisTest01 { private static UserMapper userMapper; @BeforeClass public static void berofeClass() throws Exception { //1、从xml中构建SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //这里指定了环境为test // SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"test"); //build不方法不指定环境就使用默认的 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2、获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(true); //3、获取UserMapper接口的动态代理对象 userMapper = sqlSession.getMapper(UserMapper.class); } @Test public void saveUser(){ User user = new User(); user.setUserName("锁哥"); user.setAge(18); user.setName("黑旋风"); user.setPassword("1234"); user.setSex(1); userMapper.saveUser(user); } }

小结:

新增数据的注解为:@Insert ,作用等同于映射文件中的具体使用时,需要给其value属性设置具体的SQL。

2.2、删除

目标:使用注解@Delete删除id值为1的数据

步骤:

第一步:在根据id删除数据的方法上面编写注解@Delete; 第二步:测试

实现:

第一步:在UserMapper接口中的deleteUserById方法上编写@Delete,并设置其value属性值为具体的删除SQL;

/* 2.根据id删除用户 */ @Delete("delete from user where id=#{id}") void deleteUserById(Long id);

第二步:测试

@Test public void deleteUserById(){ userMapper.deleteUserById(1L); }

小结:

删除数据的注解:@Delete,作用等同于映射文件中的,具体使用时,需要设置其value属性值为具体的删除SQL;

2.3、修改

目标:修改id为1的用户的数据

步骤:

第一步:在根据id修改用户数据方法上面添加注解@Update,然后在其value属性值中编写具体的SQL; 第二步:测试

实现:

第一步:在UserMapper接口的updateUser方法上添加注解:@Update,然后将其value属性值设置成update的SQL;

/** * 3.修改用户数据 * @param user */ @Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}") void updateUser(User user);

第二步:测试

@Test public void updateUser(){ User user = new User(); user.setId(1L); user.setUserName("柳岩"); user.setSex(0); user.setPassword("3456"); user.setName("岩岩"); user.setAge(20); userMapper.updateUser(user); }

小结:修改数据的注解:@Update,作用等同于映射文件中的。

2.4、查询

目标:使用注解查询所有的用户数据

步骤:

第一步:在接口中查询所有的用户数据的方法上面添加注解:@Select,然后设置其value属性值为具体的SQL查询语句; 第二步:测试

实现:

第一步:在UserMapper接口的queryAllUsers方法上添加注解:@Select,然后设置其value属性值为具体的查询SQL;

/* * 4.查询所有用户数据 */ @Select("SELECT * FROM tb_user") List queryAllUsers();

第二步:测试

@Test public void queryAllUsers(){ List list = userMapper.queryAllUsers(); System.out.println("list = " + list); }

小结:

查询数据注解:@Select ,作用等同于映射文件中的标签。

3、返回新增数据的id(自增主键回填)了解

问题:上面注解实现CRUD的测试中,数据新增成功,但是id值没有正常返回.

image-20200608104242526

目标:使用注解完成数据新增,新增成功后返回数据的主键id值

步骤:

1、在新增数据注解 @Insert下面,添加@Options; 2、在Options注解中,设置useGeneratedKeys值为true,keyProperty为id,keyColumn id;

实现:

第一步:在新增数据注解 @Insert下面,添加@Options,设置useGeneratedKeys值为true,keyProperty为id,keyColumn 为id;

//1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量 @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})") @Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id") void saveUser(User user);

第二步:测试:

image-20200608105014890

小结:注解@Options:

1551522132041

1551522148390

4、注解实现别名映射

根据之前的学习,如果数据表的列名和pojo实体类的属性名不一致,会导致数据表的数据无法封装到实体类属性值中,对此我们又如下解决方案:

【1】查询的时候给列起别名,别名和实体类的属性名一致

select user_name as userName from tb_user where id =1;

【2】在mybatis的核心配置文件中按照如下配置:

【3】在映射文件中,我们可以通过在ResultMap中,通过result标签中,给指定的column映射到实体类中指定的属性上。

而在注解中也有相应的解决方案:这里就必须使用注解:@Results

@Results注解相当于之前映射文件中的ResultMap,该注解如下:

public @interface Results { Result[] value() default {}; }

我们发现value属于Result数组类型,而Result属于一个注解,注解的属性如下:

public @interface Result { //对应数据表的列 String column() default ""; //对应pojo类的属性 String property() default ""; //javaType:返回的对象类型 Class javaType() default void.class; //one: 一对一配置 One one() default @One; //many: 一对多配置 Many many() default @Many; }

目标:使用注解的方式给取别名后的字段,映射到实体类中

步骤:

第一步:将之前核心配置文件中的开启驼峰自动映射设置为false 第二步:在接口中查询的方法上面添加注解@Results; 第三步:测试;

实现:

第一步:将之前核心配置文件中的开启驼峰自动映射设置为false。这样才可以演示出@Results的效果。

第二步:在接口中查询方法上面添加注解:@Results ,然后通过@Result完成字段的别名和实体类的属性名之间的映射配置;

说明:这里我们使用之前的查询所用用户方法即可,也可以在接口中在定义一个根据id查询用户的方法。

/* 根据id查询用户 */ @Select("SELECT * FROM tb_user WHERE id = #{id}") @Results(value={ @Result(column = "user_name",property = "userName") }) User selectById(@Param("id") Long id);

第三步:测试

@Test public void selectById(){ User user = userMapper.selectById(1L); System.out.println("user = " + user); }

【结果】

image-20200608151611632

小结:给别名映射到实体类中可以通过添加注解:@Results 中的 @Result实现;

第三章 注解实现动态sql 1、 动态sql(理解)

【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户。

正常的sql语句:查询男性并且用户名中包含zhang

image-20210524221029129

select * from tb_user where sex = "男" and user_name like '%zhang%'

image-20210524221045235

select * from tb_user where sex = "男"

​ 实现需求时还要判断用户是否输入用户名来做不同的查询要求,而这里似乎没有办法判断是否输入了用户名,因此可以考虑使用动态sql来完成这个功能。

​ 上述动态sql语句部分: and user_name like '%zhang%'

注解开发时,Mybatis给我们提供了常见的2种编写动态sql的方式:

方式1:@SelectProvider

使用 @SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用命名参数。

接口:

@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL") List queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);

动态sql生成类

package com.itheima.sh.utils; import org.apache.ibatis.annotations.Param; public class ProviderUtils { public String queryUsersBySexOrUsernameSQL(@Param("sex") String sex, @Param("username") String username){ String sql = "select * from tb_user where sex = #{sex}"; if (username!=null && !"".equals(username)){ sql += " and user_name like concat('%',#{username},'%')"; } return sql; } }

测试类:

@Test public void queryUsersBySexOrUsername() { List list = mapper.queryUsersBySexOrUsername("男", "lisi"); System.out.println("list = " + list); } 方式2:@SelectProvider和SQL类

接口:

@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL2") List queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);

动态sql生成类:

借助mybatis提供的一个对象:SQL。

创建SQL对象

SQL sql = new SQL();

链式编程,每个方法返回值都是SQL类对象

public class ProviderUtils { public String queryUsersBySexOrUsernameSQL2(@Param("sex") String sex, @Param("username") String username) { //借助mybatis提供的一个对象:SQL //创建SQL对象 SQL sql = new SQL(); //链式编程,每个方法返回值都是SQL类对象 sql.SELECT("*").FROM("tb_user").WHERE("sex = #{sex}"); //判断用户是否为空,不为空就继续链式编程,即继续拼接 if(username != null && !"".equals(username)){ sql.WHERE("user_name like concat('%',#{username},'%')"); } //SELECT * FROM user WHERE (sex=? AND user_name like concat('%',?,'%')) //转换为字符串并返回 return sql.toString(); } }

测试类:

@Test public void queryUsersBySexOrUsername() { List list = mapper.queryUsersBySexOrUsername("男", "lisi"); System.out.println("list = " + list); }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有