本文共 22141 字,大约阅读时间需要 73 分钟。
第一节:输入参数和输出参数
Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。
1.2 parameterType(输入参数)
1.2.1. 传递简单类型
当传递的参数是多个的时候,容易导致mybatis混淆,这里有两种解决办法:
一种是使用MyBatis的默认名param 一种是使用自定义命名,需要在接口的参数列表中添加注解//加注解,以保证在mapper映射文件中可以使用自定义的参数名,可读性高,同时可以使用ListfindByNameAndGender(@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 Listids; //一个集合参数,保留的是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接口中添加方法
ListfindUserQueryVo(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)); // 执行查询 Listlist = 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,或者是orderbyListfindByPage(Map map);
在UserMapperTest增加测试方法
@Testpublic void testFindByPage() { SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Mapmap = 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 { ListqueryOrderUser();}
编写测试方法:
@Testpublic void testQueryOrderUser() { SqlSession sqlSession = MyBatisUtils.openSession(); OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); Listorders = mapper.queryOrderUser(); for (Order order : orders) { System.out.println(order.toString() + "====" + order.getUser().toString()); } sqlSession.close();}
1.4.3 查询用户表对应的订单表,一对多
xml映射文件
接口方法
ListfindUserOrder ();
测试方法
@Testpublic void testFindUserOrder() { SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Listusers = 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 Listids; //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; }}
接口
ListfindByIds(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)); Listusers = 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中
配置如下,注意相关位置改为自己的东西:
- 修改要生成的数据库表
- pojo文件所在包路径
- Mapper所在的包路径
4.3编写测试方法
这样,对应的mapper和pojo中的东西都生成了
public class UserTest { @Test public void testFindAll() { SqlSession sqlSession = MyBatisUtils.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); //当参数为null表示无条件查询,也就是查询所有 Listusers = 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(); }}
注意:
- 生成器生成的代码只能做单表查询
- 不能在生成的代码上进行扩展,因为如果数据库变更,需要重新使用逆向工程生成代码,原来编写的代码就被覆盖了。
- 一张表会生成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 ListqueryAll(); @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); Listscores = 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); ListscoreList = 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进行结合使用
注意事项:
- 【prefixOverrides】属性的值,【AND】后面必须有空格
- 字符串,进行判断的时候,必须进行两重判断,一种是不为null,一种是不为"",使用【and】联合判断
- 字符串判断,like后面跟着的是值,如果想模糊判断,只能在service层或者controller层进行字符串拼接,加上%
7.2 foreach 循环语句
动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。
案例一:根据id数组进行遍历查询
ListfindByIds(@Param("ids") Integer[] ids);
案例一:循环遍历添加数据
void add(@Param("students") Liststudents);
insert into student(stuname,age) values (#{stu.stuname},#{stu.age})
注意事项
- collection属性是参数@param的值
- item的值前后要一致,用法与Jstl一致
- open和close是将【整个】foreach循环包裹,而不是没循环一次包裹一次,所以案例二要舍弃open和close
第八节:六表联查
用户-中间表-角色表-中间表-权限菜单表-子菜单表
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
我们根据用户id去查询对应的菜单,可以一次查完。
注意起别名了,这样才能将查到的所有sm2的数据封装到Menu中的menuList集合中
还要注意,sm的menuId要与sm2的parentId进行关联
转载地址:https://blog.csdn.net/qq_45337431/article/details/101032560 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!