Mybatis-高级应用
发布日期:2021-07-27 04:56:16 浏览次数:7 分类:技术文章

本文共 22141 字,大约阅读时间需要 73 分钟。

第一节:输入参数和输出参数

Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。

1.2 parameterType(输入参数)

1.2.1. 传递简单类型

当传递的参数是多个的时候,容易导致mybatis混淆,这里有两种解决办法:

一种是使用MyBatis的默认名param
一种是使用自定义命名,需要在接口的参数列表中添加注解

//加注解,以保证在mapper映射文件中可以使用自定义的参数名,可读性高,同时可以使用List
findByNameAndGender(@Param("username") String username, @Param("gender") String gender);
1.2.2. 传递pojo对象

参考

1.2.3. 传递pojo包装对象

开发中可以使用pojo传递查询条件。

查询条件可能是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如查询用户信息的时候,将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。

包装对象:Pojo类中的一个属性是另外一个pojo。

需求1:根据用户名模糊查询用户信息

查询条件放到QueryVo的user属性中。

编写QueryVo类

public class QueryVo {
private User user; //另一个pojo对象 private List
ids; //一个集合参数,保留的是id号的集合,后面会用到,可以指定查询多个 public User getUser() {
return user; } public void setUser(User user) {
this.user = user; } public List
getIds() {
return ids; } public void setIds(List
ids) {
this.ids = ids; }}

UserMapper.xml配置

在UserMapper接口中添加方法

List
findUserQueryVo(QueryVo queryVo);

在UserMapperTest增加测试方法

@Testpublic void testQueryUserByQueryVo() {
// MyBatis和spring整合,整合之后,交给spring管理 SqlSession sqlSession = MyBatisUtils.openSession(); // 创建Mapper接口的动态代理对象,整合之后,交给spring管理 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 使用userMapper执行查询,使用包装对象 QueryVo queryVo = new QueryVo(); // 设置到包装对象中,其他无关属性,直接Null queryVo.setUser(new User(null, "%鬼%", null, null, null)); // 执行查询 List
list = userMapper.findUserByQueryVo(queryVo); for (User u : list) {
System.out.println(u); } sqlSession.close();}
1.2.4. 传递map集合

需求2:分页查询用户信息

分页属性放入map集合中,注意:map的key要和sql中的占位符保持名字一致。

在UserMapper.xml中配置sql

#号【?】应用于数据,$应用于字段名

在UserMapper接口中添加方法

//分页查询,String表示传入偏移量offset还是pageSize,或者是orderbyList
findByPage(Map
map);

在UserMapperTest增加测试方法

@Testpublic void testFindByPage() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map
map = new HashMap<>(); map.put("offset", 0); //偏移量是0,相当于是【(pageNum-1)*pageSize】 map.put("pageSize", 2); //这里对应mapper中的字段查询,这里的$符占位的字段位置就会换成【order by id】 map.put("orderby", "order by id"); List
users = mapper.findByPage(map); for (User user : users) {
System.out.println(user.toString()); } sqlSession.close();}

1.3. resultType(输出参数)

1.3.1. 输出简单类型

需求:查询用户表数据条数

在UserMapper.xml中配置sql

在UserMapper添加方法

long getCount();

在UserMapeprTest增加测试方法

@Testpublic void testGetCount() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); long count = mapper.getCount(); System.out.println(count); sqlSession.close();}

注意:输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型

1.3.2. 输出pojo对象 和 输出pojo列表

参考

1.4 resultMap,输出集合

1.4.1 起别名,处理数据库列名和java对象属性名不一致问题

一般情况下,数据库中的列名往往与pojo对象的属性名不一致,比如order表中,用户id是【user_id】下划线命名,而实际的对象属性是【userId】驼峰命名。

这时候我们可以用三种办法解决,一种是通过sql语句另起别名。

另一种办法就是采用集合resaultMap

最后的解决办法就是在MyBatis-config.xml文件中配置

1.4.2 查询订单表order的所有数据,一对一

resultType可以指定将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。

如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。

resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。

Order对象:

public class Order {
private Integer id; private Integer userId; private String number; private Date createTime; private String note; private User user;//建立一个User对象 //get/set。。。}

创建OrderMapper.xml配置文件

接口

public interface OrderMapper {
List
queryOrderUser();}

编写测试方法:

@Testpublic void testQueryOrderUser() {
SqlSession sqlSession = MyBatisUtils.openSession(); OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); List
orders = mapper.queryOrderUser(); for (Order order : orders) {
System.out.println(order.toString() + "====" + order.getUser().toString()); } sqlSession.close();}
1.4.3 查询用户表对应的订单表,一对多

xml映射文件

接口方法

List
findUserOrder ();

测试方法

@Testpublic void testFindUserOrder() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List
users = mapper.findUserOrder(); for (User user : users) {
System.out.println(user.toString() + "====订单个数" + user.getOrders().size()); } sqlSession.close();}
1.4.4 除了使用连表查询,还可以使用懒加载方式

1.5 类型转换器

每当MyBatis设置参数到PrepareStatement或者从ResultSet结果集中取值时,就会用到TypeHandler来处理数据库类型与Java类型之间的转换。

myBatis类型转换器适用于 Java实体类中的类型和数据库中的类型不对应时。

下图是默认的TypeHandler:

在这里插入图片描述

案例:假如User中包含一个对象属性Address,如何把数据库中address转成Address对象呢?

User类和Address类

public class User {
/* * `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(32) NOT NULL COMMENT '用户名称', `birthday` DATE DEFAULT NULL COMMENT '生日', `sex` CHAR(1) DEFAULT NULL COMMENT '性别', `address` VARCHAR(256) DEFAULT NULL COMMENT '地址', * */ private Integer id; private String username; private Date birthday; private String gender; private Address address; //引入一个Address类 get/set...} public class Address {
private String address; public Address(String string){
this.address=string; } public String getAddress() {
return address; } public void setAddress(String address) {
this.address = address; } @Override public String toString() {
return "Address{" + "address='" + address + '\'' + '}'; }}

在数据库中,address是String 类型,如果用一般的方法,则打印结果如下

//查询方法findALl,地址都为空User{
id=1, username='王五', birthday=null, gender='2', address='null'}User{
id=2, username='水龙在天', birthday=Thu Sep 19 00:00:00 CST 2019, gender='男', address='null'}User{
id=3, username='哈哈', birthday=Wed Sep 11 00:00:00 CST 2019, gender='女', address='null'}User{
id=4, username='鬼头', birthday=Tue Sep 03 00:00:00 CST 2019, gender='男', address='null'}User{
id=5, username='鬼神', birthday=Wed Sep 04 00:00:00 CST 2019, gender='女', address='null'}User{
id=10, username='张三', birthday=Thu Jul 10 00:00:00 CST 2014, gender='1', address='null'}//添加方法add,异常Caused by: java.io.NotSerializableException: com.rj.pojo.Address

解决办法就是使用转换器TypeHandler,创建一个类遵从TypeHandler接口,并实现setParameter和getResult方法,这里需要复习一下前面的JDBC操作

public class AddressHandler implements TypeHandler
{
//注入的时候,将对象转为字符串 @Override public void setParameter(PreparedStatement ps, int i, Address parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.getAddress()); } else {
ps.setString(i, null); } } //获取的时候,将字符串转为对象 @Override public Address getResult(ResultSet rs, String columnName) throws SQLException {
String string = rs.getString(columnName); Address address = new Address(string); return address; } @Override public Address getResult(ResultSet rs, int columnIndex) throws SQLException {
return null; } @Override public Address getResult(CallableStatement cs, int columnIndex) throws SQLException {
return null; }}

需要在mybatis-config.xml文件中进行配置

这样就可以解决问题了

第二节:动态sql(重点)

2.1 where/if/foreach

当我们需要多条件查询的时候,需要进行多个“and”语句判断,但是,我们不能将每个调节都直接固定下来,如果在xml的sql语句中我们固定了三个调节,而实际传入的只有一个条件的值,则会发生异常,或者查询不到,这时需要使用 if 判断,语法类似于JSTL

这里的【where】相当于是【where 1=1】为了避免sql语句错误,另外,模糊查询的【%】语句必须在servlet部分进行拼接好传入,否则是精确查找。

此外,我们还可以使用循环,根据多个id查询用户信息。

QueryVo类

public class QueryVo {
private User user; private List
ids; //ids属性,获取id值集合 public User getUser() {
return user; } public void setUser(User user) {
this.user = user; } public List
getIds() {
return ids; } public void setIds(List
ids) {
this.ids = ids; }}

接口

List
findByIds(QueryVo vo);List
findByIds2(List
list);List
findByIds3(Integer[] arr);

xml映射文件的配置

测试

@Test    public void testFindIds() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); QueryVo queryVo = new QueryVo(); queryVo.setIds(Arrays.asList(16,22,24)); List
users = mapper.findByIds(queryVo); for (User user : users) {
System.out.println(user.toString()); } sqlSession.close(); } @Test public void testFindIds2() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List
list = Arrays.asList(10, 16, 22); List
users = mapper.findByIds2(list); for (User user : users) {
System.out.println(user.toString()); } sqlSession.close(); } @Test public void testFindIds3() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Integer[] arr = {
10,16,22}; List
users = mapper.findByIds3(arr); for (User user : users) {
System.out.println(user.toString()); } sqlSession.close(); }

2.3 Sql片段

Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的(其实没个卵用)

select * from user

第三节:Mybatis自动生成器

使用官方网站的Mapper自动生成工具mybatis-generator-core-1.3.7来生成po类和Mapper映射文件

官网:

gitup网址

3.1 pom.xml文件中添加generator插件

插件依赖信息

org.mybatis.generator
mybatis-generator-maven-plugin
1.3.7
Generate MyBatis Artifacts
generate
mysql
mysql-connector-java
5.1.45

4.2添加加配置文件

在main\resources下在添加generatorConfig.xml中

配置如下,注意相关位置改为自己的东西:

  1. 修改要生成的数据库表
  2. pojo文件所在包路径
  3. Mapper所在的包路径

4.3编写测试方法

这样,对应的mapper和pojo中的东西都生成了

public class UserTest {
@Test public void testFindAll() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); //当参数为null表示无条件查询,也就是查询所有 List
users = mapper.selectByExample(null); for (User user : users) {
System.out.println(user.toString()); } sqlSession.close(); } @Test public void tesetFindByUsername() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); //条件查询,需要使用UserExample的pojo类对象创建条件 UserExample userExample = new UserExample(); //一点到底操作 userExample.createCriteria() .andUsernameLike("%鬼%") .andGenderEqualTo("男"); List
users = mapper.selectByExample(userExample); for (User user : users) {
System.out.println(user.toString()); } } @Test public void testAdd() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setUsername("姬昌"); user.setGender("男"); //mapper.insert(user); mapper.insertSelective(user); //这种方式,当数据库的参数没有得到对应的传入值,则该列列名直接从sql语句中去除 sqlSession.commit(); sqlSession.close(); } /** * insert(user);打印结果 * Preparing: insert into user (id, username, birthday, gender, address) values (?, ?, ?, ?, ?) * * insertSelective(user);打印结果 * Preparing: insert into user ( username, gender ) values ( ?, ? ) */ @Test public void testFindAll2() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class); List
all = mapper.findAll(); for (User user : all) {
System.out.println(user.toString()); } sqlSession.close(); } @Test public void testFindById() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class); User user = mapper.findById(10); System.out.println(user.toString()); sqlSession.close(); } @Test public void testAdd2() {
SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class); User user = new User(); user.setUsername("问天"); user.setGender("男"); user.setAddress("昆仑"); mapper.add2(user); System.out.println(user.toString()); sqlSession.commit(); sqlSession.close(); } @Test public void testUpdate(){
SqlSession sqlSession= MyBatisUtils.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user=new User(); user.setId(31); user.setUsername("xxxxxxxx"); //user.setAddress("上海"); user.setBirthday(new Date()); userMapper.updateByPrimaryKey(user); sqlSession.commit(); sqlSession.close(); }}

注意:

  1. 生成器生成的代码只能做单表查询
  2. 不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了。
  3. 一张表会生成4个文件

第五节:MyBatis的注解开发方式(了解)

从MyBatis3开始,注解提供了一种简单的方式来实现简单映射语句,这种方式不会引入大量的开销。但是灵活性较差,不易扩展。

Mybatis常用注解对应的目标和标签如表所示:

注解 目标 对应的XML标签
@Select 方法 <select>
@Insert 方法 <insert>
@Update 方法 <update>
@Delete 方法 <delete>
@Param 参数
@SelectKey 方法 <selectKey>
@One 方法 <association>
@Many 方法 <collection>

示例如下:

/** * wgy 2019/7/5 15:24 */public interface UserMapper {
@Select(value = "select * from user where id=#{id}") public User queryById(Integer id); @Select(value = "select * from user") public List
queryAll(); @Insert(value = "insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address});") @SelectKey(keyProperty = "id",before = false,resultType = Integer.class,statement = "select LAST_INSERT_ID()") public void saveUser(User user); @Select(value = "select * from user where username like #{userna me} and sex=#{sex}") public List
findByWhere(@Param("username") String username, @Param("sex") String sex); @Update(value = "update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}") public void updateUser(User user);}/** * 使用mybatis注解配置sql映射 */public interface UserMapper2 {
@Select(value = "select * from user") List
findAll(); @Select("select * from user where id=#{id}") User findById(Integer id); //增加,同时获取id @Insert(value = "insert into user(username,birthday,gender,address) values(#{username},#{birthday},#{gender},#{address})") @SelectKey(keyProperty = "id",resultType = Integer.class, before = false,keyColumn = "id",statement = "select LAST_INSERT_ID()") void add2(User user);}

第六节:MyBatis三表联查

1. 饿汉方式

这里我们定义简单的学生信息、课程信息和成绩单三个表,学生信息和课程信息是多对多的关系,一个学生可以有多个课程,一个课程可以被多个学生选择,这里的成绩单就是中间件。

在从学生表角度出发,与成绩单就是一对多的关系,从成绩单的角度出发,与课程表就是一对一的关系,所以我们完全可以在mapper映射文件中这样书写resultMap。

这种方式,必须在实体类pojo中注入相关的属性,比如在Student类中必须添加Score的list属性以及set/get方法,在Score中必须添加Course属性以及set/get方法。

//测试的时候,由于在mapper中返回的是stumap结果,所以必须一层层调取public class StudentTest {
@Test public void testQuerryById() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); StudentDao studentDao = (StudentDao) context.getBean("studentDao"); Student student = studentDao.querryById(2); List
scores = student.getScore(); for (Score score : scores) {
System.out.println("学生编号:" + student.getStuid() + ",姓名:" + student.getStuname() + ",年龄:" + student.getAge() + ",所选课程:" + score.getCourse().getCoursename() + ",课程成绩:" + score.getScore()); } }}
2. 懒加载方式

主要是调取方法,根据colmn属性值去查询自己的表,返回数据,根据需求才去查询,这样提高性能,而不需要写很多的sql语句。

但是不能层层嵌套,需要分开来写,即在StudentDao.xml中配置student和score,然后在ScoreDao.xml中配置score和course,这样,一层层的调取。

StudentDao.xml

ScoreDao.xml

3. 在service层进行处理,类似于懒加载,只是方法是在业务层调用的

业务层

@Servicepublic class StudentServiceImpl implements StudentService {
@Autowired private ScoreDao scoreDao; @Autowired private StudentDao studentDao; @Autowired private CourseDao courseDao; @Override public Student findById(Integer stuid) {
//一层层的调取方法,然后通过set方法放入到对应的对象中 Student student = studentDao.querryById(stuid); List
scoreList = scoreDao.findByStuid(stuid); for (Score score : scoreList) {
Course course = courseDao.findByCourseId(score.getCourseid()); score.setCourse(course); } student.setScore(scoreList); return student; }}

第七节:常用动态sql和注意事项

7.1 trim+where+if 格式

给方式是最常用的条件查询方式,直接使用<where></where>但是有时候where标签会不起作用这里最好是使用trim进行结合使用

注意事项

  1. 【prefixOverrides】属性的值,【AND】后面必须有空格
  2. 字符串,进行判断的时候,必须进行两重判断,一种是不为null,一种是不为"",使用【and】联合判断
  3. 字符串判断,like后面跟着的是值,如果想模糊判断,只能在service层或者controller层进行字符串拼接,加上%

7.2 foreach 循环语句

动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。

案例一:根据id数组进行遍历查询

List
findByIds(@Param("ids") Integer[] ids);

案例一:循环遍历添加数据

void add(@Param("students") List
students);
insert into student(stuname,age) values
(#{stu.stuname},#{stu.age})

注意事项

  1. collection属性是参数@param的值
  2. item的值前后要一致,用法与Jstl一致
  3. open和close是将【整个】foreach循环包裹,而不是没循环一次包裹一次,所以案例二要舍弃open和close

第八节:六表联查

用户-中间表-角色表-中间表-权限菜单表-子菜单表

一般,菜单表中会有menuId和parentId,这样将子菜单也记录在内了,这里type属性表示目录级别。

定义pojo,包含子菜单list集合

public class Menu {
private Integer menuId; private Integer parentId; private String name; private String url; private String perms; private Integer type; private String icon; private Integer orderNum; private List
list; //存储子菜单...

我们根据用户id去查询对应的菜单,可以一次查完。

注意起别名了,这样才能将查到的所有sm2的数据封装到Menu中的menuList集合中

还要注意,sm的menuId要与sm2的parentId进行关联

转载地址:https://blog.csdn.net/qq_45337431/article/details/101032560 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:MyBatis【进阶详解】
下一篇:MyBatis初识

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年09月13日 12时07分37秒