MyBatis基础学习笔记 学习来源:尚硅谷
学习时间:2022年6月3日端午节,2023年6月24日端午节复习
1 MyBatis简介 1.1 历史 MyBatis最初是Apache的一个开源项目iBatis , 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。
随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis
。
代码于2013年11月迁移到Github。iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架 。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。
1.2 特性
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架(对象关系映射)
1.3 与其它持久层框架对比
JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生产的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于Hibernate,但是完全能够接受
2 搭建MyBatis 2.1 创建maven工程 创建一个空的工程MyBatisDemo
。然后再新建一个基于maven的module。
设置打包方式为jar:
1 2 3 4 <groupId > com.hongyi</groupId > <artifactId > mybatis_demo1</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > jar</packaging >
引入依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.9</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.22</version > </dependency >
2.2 核心配置文件 习惯上 命名为mybatis-config.xml
,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略 ,所以大家操作时可以直接复制、粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息。
核心配置文件存放的位置是src/main/resources
目录下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/MyBatis" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/UserMapper.xml" /> </mappers > </configuration >
2.3 mapper接口 在数据库test下新建表t_user
。
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口 ,我们不需要提供实现类。
User实体类,位于pojo
包下
1 2 3 4 5 6 7 8 9 @Data public class User { private Integer id; private String username; private String password; private Integer age; private String sex; private String email; }
UserMapper接口,位于mapper
包下
1 2 3 public interface UserMapper { int insertUser () ; }
2.4 映射文件 相关概念:ORM (O bject R elationship M apping)对象关系映射。
对象:Java的实体类对象
关系:关系型数据库
映射:二者之间的对应关系
Java概念
数据库概念
类
表
属性
字段/列
对象
记录/行
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.hongyi.mapper.UserMapper" > <insert id ="insertUser" > insert into t_user values(null,'张三','123',23,'女') </insert > </mapper >
项目结构:
2.5 测试增删改查功能
测试功能——增
在测试集上新建com.hongyi.test.MybatisTest.java
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class MybatisTest { @Test public void test0 () throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int result = mapper.insertUser(); sqlSession.commit(); System.out.println("result: " + result); } }
SqlSession
:代表Java程序和数据库 之间的会话 。(HttpSession是Java程序和浏览器之间的会话)
SqlSessionFactory
:是“生产”SqlSession的“工厂”。
工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
优化1
将事务提交设置为自动提交:
1 SqlSession sqlSession = sqlSessionFactory.openSession(true );
优化2——加入log4j日志功能
1 2 3 4 5 6 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j :configuration SYSTEM "log4j.dtd" > <log4j:configuration xmlns:log4j ="http://jakarta.apache.org/log4j/" > <appender name ="STDOUT" class ="org.apache.log4j.ConsoleAppender" > <param name ="Encoding" value ="UTF-8" /> <layout class ="org.apache.log4j.PatternLayout" > <param name ="ConversionPattern" value ="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout > </appender > <logger name ="java.sql" > <level value ="debug" /> </logger > <logger name ="org.apache.ibatis" > <level value ="info" /> </logger > <root > <level value ="debug" /> <appender-ref ref ="STDOUT" /> </root > </log4j:configuration >
注:IDEA配置xml文件头报错:URI is not registered (Settings | Languages & Frameworks | Schemas and DTDs)解决方法
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细
打印内容:
1 2 3 4 DEBUG 06-04 11:41:45,873 ==> Preparing: insert into t_user values(null, '张三', '123' ,23, '女', '123456@163.com') (BaseJdbcLogger.java:137) DEBUG 06-04 11:41:45,912 ==> Parameters: (BaseJdbcLogger.java:137) DEBUG 06-04 11:41:45,914 <== Updates: 1 (BaseJdbcLogger.java:137) result: 1
测试功能——修改和删除数据
1 2 3 4 5 6 public interface UserMapper { void updateUser () ; void deleteUser () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.hongyi.mapper.UserMapper" > <update id ="updateUser" > update t_user set username='李四' where id = 8 </update > <delete id ="deleteUser" > delete from t_user where id = 8 </delete > </mapper >
测试类略
测试功能——查询数据
查询功能的标签必须设置resultType
或resultMap
,其作用是告知Mybatis将得到的结果集封装成java中的哪个对象,即用于设置实体类和数据库表的映射关系。
resultType
属性:设置默认的映射关系,例如数据库的字段名和类对象的属性名一致的时候
resultMap
属性:设置自定义的映射关系,用于一对多或多对一或字段名和属性名不一致的情况
代码片段如下:
1 2 3 4 5 public interface UserMapper { User getUserById () ; List<User> getAllUser () ; }
1 2 3 4 5 6 7 <select id ="getUserById" resultType ="com.hongyi.pojo.User" > select * from t_user where id = 1 </select > <select id ="getAllUser" resultType ="com.hongyi.pojo.User" > select * from t_user </select >
1 2 3 4 5 6 7 User user = mapper.getUserById();System.out.println(user); List<User> users = mapper.getAllUser(); for (User u : users) { System.out.println(u); }
3 核心配置文件 3.1 详解 要求:了解即可。
核心配置文件中的标签必须按照固定的顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//MyBatis.org//DTD Config 3.0//EN" "http://MyBatis.org/dtd/MyBatis-3-config.dtd" > <configuration > <properties resource ="jdbc.properties" > </properties > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="lazyLoadingEnabled" value ="true" /> </settings > <typeAliases > <package name ="com.atguigu.mybatis.bean" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="UserMapper.xml" /> <package name ="com.atguigu.mybatis.mapper" /> </mappers > </configuration >
例如在resources
新建jdbc.properties
:
1 2 3 4 jdbc.driver =com.mysql.cj.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/test?characterEncoding=utf8 jdbc.username =root jdbc.password =12345678
3.2 封装工具 封装SqlSessionUtils
工具类并测试功能。
新建utils.SqlSessionUtils.java
1 2 3 4 5 6 7 8 9 10 11 12 13 public class SqlSessionUtils { public static SqlSession getSqlSession () { SqlSession sqlSession = null ; try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); sqlSession = sqlSessionFactory.openSession(true ); } catch (IOException e) { e.printStackTrace(); } return sqlSession; } }
测试:
1 2 3 4 5 6 7 8 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); List<User> users = mapper.getAllUser(); users.forEach(System.out::println); }
4 MyBatis获取参数值的两种方式 Mapper
接口中的某些方法需要参数,例如根据用户名查询用户,此时,用户名即为方法的参数:
1 User getUserByUsername (String username) ;
因此,需要MyBatis根据传入的参数username
来生成SQL语句。
4.1 概述 MyBatis获取参数值的两种方式:${}
和#{}
${}
的本质就是字符串拼接,#{}
的本质就是占位符赋值
${}
使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
#{}
使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值 时,可以自动添加单引号 (但也导致了一些特殊sql不能使用这种方式,参见[[#6.1 模糊查询]])
经验:能用#{}
的地方就尽量用#{}
,只有特殊sql执行时才使用${}
4.2 单个字面量类型的参数 若mapper接口中的方法参数为单个的字面量类型,此时可以使用${}
和#{}
以任意的名称 获取参数的值,注意${}
需要手动加单引号 。
1 2 3 public interface ParameterMapper { User getUserByUsername (String username) ; }
1 2 3 4 5 <select id ="getUserByUsername" resultType ="User" > select * from t_user where username = '${username}' </select >
4.3 多个字面量类型的参数 若mapper接口中的方法参数为多个时:
此时MyBatis会自动将这些参数按位置顺序放在一个map集合中:
以arg0,arg1...
为键,以参数为值;
以param1,param2...
为键,以参数为值;
两种方式都可以使用。
因此只需要通过${}
和#{}
访问map集合的键 就可以获取相对应的值 ,注意${}
仍然需要手动加单引号 。
代码示例
1 2 3 4 5 public interface ParameterMapper { User checkLogin (String username, String password) ; }
1 2 3 4 <select id ="checkLogin" resultType ="User" > select * from t_user where username = #{param1} and password=#{param2} </select >
1 2 3 4 5 6 7 @Test public void test2 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); User user = mapper.checkLogin("Mark" , "123456" ); System.out.println(user); }
4.4 map集合类型的参数 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中。
只需要通过${}
和#{}
访问map集合的键就可以获取相对应的值 ,注意${}
需要手动加单引号。
这里的键就是自己来设置,而不是mybatis来自动设置的。
代码示例
1 2 3 4 5 public interface ParameterMapper { User checkLoginByMap (Map<String, Object> map) ; }
1 2 3 <select id ="checkLoginByMap" resultType ="User" > select * from t_user where username = #{username} and password = #{password} </select >
1 2 3 4 5 6 7 8 9 10 @Test public void test3 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); Map<String, Object> map = new HashMap <>(); map.put("username" , "Mark" ); map.put("password" , "123456" ); User user = mapper.checkLoginByMap(map); System.out.println(user); }
注意,SQL语句中的username
等名字必须和map.put()
的键相同。
4.5 实体类类型的参数 若mapper接口中的方法参数为实体类对象时:
此时可以使用${}
和#{}
,通过访问实体类对象中的属性名 获取属性值,注意${}
需要手动加单引号。
代码示例
1 2 3 4 5 public interface ParameterMapper { int insertUser (User user) ; }
1 2 3 <insert id ="insertUser" > insert into t_user values(null, #{username}, #{password}, #{age}, #{sex}, #{email}) </insert >
注意,这里的username
等名字必须和类中属性名保持一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test4 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); User user = new User (); user.setUsername("Hongyi" ); user.setAge(24 ); user.setSex("男" ); user.setEmail("12345678@qq.com" ); user.setPassword("12345678" ); int i = mapper.insertUser(user); System.out.println(i); }
控制台输出:
1 2 3 DEBUG 06-04 16:39:02,483 ==> Preparing: insert into t_user values(null, ?, ?, ?, ?, ?) (BaseJdbcLogger.java:137) DEBUG 06-04 16:39:02,520 ==> Parameters: Hongyi(String), 12345678(String), 24(Integer), 男(String), 12345678@qq.com(String) (BaseJdbcLogger.java:137) DEBUG 06-04 16:39:02,531 <== Updates: 1 (BaseJdbcLogger.java:137)
4.6 使用@Param标识参数 可以通过@Param
注解标识mapper接口中的方法参数:
此时,会将这些参数放在map集合中,以@Param
注解的value属性值为键 ,以参数为值;未指定value属性时,默认以param1,param2...
为键,以参数为值;只需要通过${}
和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号。
代码示例
1 2 3 4 5 public interface ParameterMapper { User checkLoginByParam (@Param("a") String username, @Param("b") String password) ; }
1 2 3 <select id ="checkLoginByParam" resultType ="User" > select * from t_user where username = #{a} and password = #{b} </select >
1 2 3 4 5 6 7 @Test public void test5 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); User user = mapper.checkLoginByParam("Mark" , "123456" ); System.out.println(user); }
建议
第1,2,3种情况全部使用第5种情况(@Param
)解决。
第4种情况单独解决。
因此可只归结为使用实体类属性和使用@Param
注解两种情况
5 MyBatis的查询功能 5.1 查询一个实体类对象或一个list集合
若查询出的数据只有一条:
可以通过实体类对象接收
可以通过list集合接收
可以通过map集合接收(5.3小节
)
若查询出的数据有多条:
可以通过list接收,但不能通过实体类对象来接收
可以通过map的list来接收(5.4小节
)
可以在mapper接口的方法上添加@MapKey
注解,此时就可以将每条数据转换的map集合作为值 ,以某个字段为键 ,放在同一个map集合中(5.4小节
)
代码示例
1 2 3 4 5 6 7 8 9 10 11 public interface SelectMapper { User getUserById (@Param("id") Integer id) ; List<User> getAllUser () ; }
1 2 3 4 5 6 7 8 9 10 <mapper namespace ="com.hongyi.mapper.SelectMapper" > <select id ="getUserById" resultType ="User" > select * from t_user where id = #{id} </select > <select id ="getAllUser" resultType ="User" > select * from t_user </select > </mapper >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class SelectMapperTest { @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); User user = mapper.getUserById(1 ); System.out.println(user); } @Test public void test1 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); List<User> users = mapper.getAllUser(); users.forEach(System.out::println); } }
5.2 查询单个数据 在MyBatis中,对于Java中常用的类型都设置了类型别名
java.lang.Integer
–>int|integer
int
–>_int|_integer
Map
–>map
,List
–>list
代码示例
1 2 3 <select id ="getCount" resultType ="java.lang.Integer" > select count(*) from t_user </select >
注:这里的resultType
也可以写成:int
或Integer
1 2 3 4 5 6 7 @Test public void test2 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); Integer count = mapper.getCount(); System.out.println(count); }
5.3 查询一条数据为map集合
代码示例
1 2 3 4 Map<String, Object> getUserByIdToMap (@Param("id") Integer id) ;
1 2 3 <select id ="getUserByIdToMap" resultType ="map" > select * from t_user where id = #{id} </select >
注意这里的resultType
1 2 3 4 5 6 7 @Test public void test3 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); Map<String, Object> map = mapper.getUserByIdToMap(1 ); System.out.println(map); }
打印结果:
1 {password=123456, sex=男, id=1, age=24, email=123456@qq.com, username=Mark}
5.4 查询多条数据为map集合
方式一:将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取 。
代码示例
1 2 3 4 List<Map<String, Object>> getAllUserToMap () ;
1 2 3 <select id ="getAllUserToMap" resultType ="map" > select * from t_user </select >
1 2 3 4 5 6 7 @Test public void test4 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); List<Map<String, Object>> list = mapper.getAllUserToMap(); list.forEach(System.out::println); }
打印结果:
1 2 3 4 {password=123456, sex=男, id=1, age=24, email=123456@qq.com, username=Mark} {password=123, sex=女, id=6, age=23, email=123456@163.com, username=张三} {password=123, sex=女, id=7, age=23, email=123456@163.com, username=张三} {password=12345678, sex=男, id=9, age=24, email=12345678@qq.com, username=Hongyi}
方式二:将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey
注解设置map集合的键,值是每条数据所对应的map集合
代码示例
1 2 3 4 5 @MapKey("id") Map<String, Object> getAllUserToMap () ;
1 2 3 <select id ="getAllUserToMap" resultType ="map" > select * from t_user </select >
1 2 3 4 5 6 7 @Test public void test4 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SelectMapper mapper = sqlSession.getMapper(SelectMapper.class); Map<String, Object> map = mapper.getAllUserToMap(); System.out.println(map); }
打印结果:
1 {1={password=123456, sex=男, id=1, age=24, email=123456@qq.com, username=Mark}, 6={password=123, sex=女, id=6, age=23, email=123456@163.com, username=张三}, 7={password=123, sex=女, id=7, age=23, email=123456@163.com, username=张三}, 9={password=12345678, sex=男, id=9, age=24, email=12345678@qq.com, username=Hongyi}}
6 特殊SQL的执行 6.1 模糊查询 使用#{}
时,执行模糊查询会使用?
进行占位,在单引号中会被解析为字符串,因此占位符失效:
1 2 3 4 List<User> getUserByLike (@Param("username") String username) ;
1 2 3 <select id ="getUserByLike" resultType ="User" > select * from t_user where username like '%${username}%' </select >
1 2 3 4 5 6 7 8 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SQLMapper mapper = sqlSession.getMapper(SQLMapper.class); List<User> list = mapper.getUserByLike("三" ); list.forEach(System.out::println); }
1 2 3 4 <select id ="getUserByLike" resultType ="User" > select * from t_user where username like concat('%', #{username}, '%') </select >
1 2 3 <select id ="getUserByLike" resultType ="User" > select * from t_user where username like "%"#{username}"%" </select >
6.2 批量删除 只能够使用${}
,因为#{}
会自动对字符串添加单引号,导致sql语句不正确。
1 2 3 4 int deleteMore (@Param("ids") String ids) ;
1 2 3 <delete id ="deleteMore" > delete from t_user where id in(${ids}) </delete >
1 2 3 4 5 6 7 8 @Test public void test1 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SQLMapper mapper = sqlSession.getMapper(SQLMapper.class); int result = mapper.deleteMore("1, 2, 3" ); System.out.println(result); }
6.3 动态设置表名 只能够使用${}
,因为#{}
会自动对字符串添加单引号,导致sql语句不正确。
1 2 3 4 List<User> getUserByTableName (@Param("tableName") String tableName) ;
1 2 3 <select id ="getUserByTableName" resultType ="User" > select * from ${tableName} </select >
1 2 3 4 5 6 7 8 @Test public void test2 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SQLMapper mapper = sqlSession.getMapper(SQLMapper.class); List<User> list = mapper.getUserByTableName("t_user" ); list.forEach(System.out::println); }
6.4 获取自增的主键 insert
标签的两个属性:
useGeneratedKeys
:设置使用自增的主键
keyProperty
:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性 中 ,例如就将其赋值给User的id属性。
代码示例
1 2 3 4 void insertUser (User user) ;
1 2 3 <insert id ="insertUser" useGeneratedKeys ="true" keyProperty ="id" > insert into t_user values(null, #{username}, #{password}, #{age}, #{sex}, #{email}) </insert >
1 2 3 4 5 6 7 8 9 @Test public void test3 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); SQLMapper mapper = sqlSession.getMapper(SQLMapper.class); User user = new User (null , "Kisugi" , "123" , 24 , "男" , "123@123.com" ); mapper.insertUser(user); System.out.println(user.getId()); }
7 自定义映射resultMap 7.1 准备工作
新建表
新建两张表:t_emp
和t_dept
,表结构如下:
实体类
1 2 3 4 5 6 7 8 9 10 @Data @NoArgsConstructor @AllArgsConstructor public class Emp { private Integer eid; private String empName; private Integer age; private String sex; private String email; }
1 2 3 4 5 6 7 @Data @NoArgsConstructor @AllArgsConstructor public class Dept { private Integer did; private String deptName; }
注意:按照开发经验,数据库中的字段名称采用下划线规则,而类中属性名称采用驼峰规则,所以二者映射并不一致。
7.2 处理字段和属性的映射关系 若字段名和实体类中的属性名不一致,则可以通过resultMap
设置自定义映射。
1 2 3 4 List<Emp> getAllEmp () ;
1 2 3 <select id ="getAllEmp" resultType ="Emp" > select eid,emp_name empName, age, sex, email from t_emp </select >
1 2 3 4 5 6 7 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); List<Emp> list = mapper.getAllEmp(); list.forEach(System.out::println); }
方法二:可以在MyBatis的核心配置文件 中设置一个全局配置信息mapUnderscoreToCamelCase
,可以在查询表中数据时,自动将_类型的字段名转换为驼峰
1 2 3 4 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
方法三:可以通过resultMap
设置自定义映射,此时需要设置所有的字段 ,这种方法最常用
resultMap
:设置自定义映射,属性有下
id
:表示自定义映射的唯一标识
type
:查询的数据要映射的实体类的类型
子标签和对应属性如下:
id
:设置主键的映射关系
result
:设置普通字段的映射关系
association
:设置多对一的映射关系(详见7.3.2
)
collection
:设置一对多的映射关系 (详见7.4.1
)
property
:设置映射关系中实体类中 的属性名
column
:设置映射关系中表中 的字段名
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="empResultMap" type ="Emp" > <id property ="eid" column ="eid" /> <result property ="empName" column ="emp_name" /> <result property ="password" column ="password" /> <result property ="age" column ="age" /> <result property ="sex" column ="sex" /> </resultMap > <select id ="getAllEmp" resultMap ="empResultMap" > select * from t_emp </select >
7.3 多对一映射处理 多个员工对应一个部门
需求:查询员工信息以及员工所对应的部门信息
1 2 3 4 5 6 7 8 9 public class Emp { private Integer eid; private String empName; private Integer age; private String sex; private String email; private Dept dept; }
7.3.1 级联方式处理映射关系 该方法不常用。
1 2 3 4 Emp getEmpAndDept (@Param("eid") Integer eid) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 <resultMap id ="empAndDeptResultMapOne" type ="Emp" > <id property ="eid" column ="eid" /> <result property ="empName" column ="emp_name" /> <result property ="password" column ="password" /> <result property ="age" column ="age" /> <result property ="sex" column ="sex" /> <result property ="dept.did" column ="did" /> <result property ="dept.deptName" column ="dept_name" /> </resultMap > <select id ="getEmpAndDept" resultMap ="empAndDeptResultMapOne" > select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid} </select >
1 2 3 4 5 6 7 @Test public void test1 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDept(1 ); System.out.println(emp); }
打印结果:
1 Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=Dept(did=1, deptName=A))
7.3.2 association处理映射关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <resultMap id ="empAndDeptResultMapTwo" type ="Emp" > <id property ="eid" column ="eid" /> <result property ="empName" column ="emp_name" /> <result property ="password" column ="password" /> <result property ="age" column ="age" /> <result property ="sex" column ="sex" /> <association property ="dept" javaType ="Dept" > <id property ="did" column ="did" /> <result property ="deptName" column ="dept_name" /> </association > </resultMap > <select id ="getEmpAndDept" resultMap ="empAndDeptResultMapTwo" > select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid} </select >
注:javaType
是属性dept
的类型,association
相当于resultMap
的套娃形式
7.3.3 分步查询 这种方法最常用。
1 2 3 4 Emp getEmpAndDeptByStepOne (@Param("eid") Integer eid) ;
1 2 3 4 5 6 7 8 9 10 11 12 <resultMap id ="empAndDeptByStepResultMap" type ="Emp" > <id property ="eid" column ="eid" /> <result property ="empName" column ="emp_name" /> <result property ="password" column ="password" /> <result property ="age" column ="age" /> <result property ="sex" column ="sex" /> <association property ="dept" select ="com.hongyi.mapper.DeptMapper.getEmpAndDeptByStepTwo" column ="did" /> </resultMap > <select id ="getEmpAndDeptByStepOne" resultMap ="empAndDeptByStepResultMap" > select * from t_emp where eid = #{eid} </select >
注意:
select
:设置分步查询,查询某个属性的值的sql的标识(namespace.sqlId)
column
:将sql以及查询结果中的某个字段设置为分步查询的条件
第二步:通过员工信息查询部门信息(在Dept的mapper接口中)
1 2 3 4 Dept getEmpAndDeptByStepTwo (@Param("did") Integer did) ;
1 2 3 <select id ="getEmpAndDeptByStepTwo" resultType ="Dept" > select * from t_dept where did = #{did} </select >
1 2 3 4 5 6 7 @Test public void test2 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1 ); System.out.println(emp); }
执行结果:可以看到连续执行了两条sql语句
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=”lazy(延迟加载)|eager(立即加载)”
1 2 3 4 <settings > <setting name ="lazyLoadingEnables" value ="true" /> </settings >
1 2 3 4 5 6 7 8 @Test public void test2 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1 ); System.out.println(emp.getEmpName()); }
打印结果:
发现只执行了一条sql语句,即第一步查询,第二步查询并未执行。
以上是在全局开启延迟加载,手动单独关闭或开启延迟加载可以如下操作,即利用fetchType=”lazy(延迟加载)|eager(立即加载)”。
1 2 3 4 5 <association property ="dept" select ="com.hongyi.mapper.DeptMapper.getEmpAndDeptByStepTwo" column ="did" fetchType ="eager" />
执行结果:
发现两条sql均执行了
7.4 一对多映射处理 一个部门对应多个员工。
需求:根据部门id查询部门以及部门中的员工信息
1 2 3 4 5 public class Dept { private Integer did; private String deptName; private List<Emp> emps; }
7.4.1 利用collection
collection
:处理一对多的映射关系
ofType
:设置collection标签所处理的集合属性中存储数据的类型 ,例如根据部门id查询到多个EMP员工,将其放入集合中
1 2 3 4 Dept getDeptAndEmp (@Param("did") Integer did) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <resultMap id ="deptAndEmpResultMap" type ="Dept" > <id property ="did" column ="did" /> <result property ="deptName" column ="dept_name" /> <collection property ="emps" ofType ="Emp" > <id property ="eid" column ="eid" /> <result property ="empName" column ="emp_name" /> <result property ="password" column ="password" /> <result property ="age" column ="age" /> <result property ="sex" column ="sex" /> </collection > </resultMap > <select id ="getDeptAndEmp" resultMap ="deptAndEmpResultMap" > select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did} </select >
1 2 3 4 5 6 7 @Test public void test3 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); Dept dept = mapper.getDeptAndEmp(1 ); System.out.println(dept); }
打印结果:
1 Dept(did=1, deptName=A, emps=[Emp(eid=1, empName=张三, age=24, sex=男, email=null, dept=null), Emp(eid=2, empName=李四, age=24, sex=男, email=null, dept=null)])
7.4.2 分步查询
1 2 3 4 Dept getDeptAndEmpByStepOne (@Param("did") Integer did) ;
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="deptAndEmpByStepResultMap" type ="Dept" > <id property ="did" column ="did" /> <result property ="deptName" column ="dept_name" /> <collection property ="emps" select ="com.hongyi.mapper.EmpMapper.getDeptAndEmpByStepTwo" column ="did" /> </resultMap > <select id ="getDeptAndEmpByStepOne" resultMap ="deptAndEmpByStepResultMap" > select * from t_dept where did = #{did} </select >
1 2 3 4 List<Emp> getDeptAndEmpByStepTwo (@Param("did") Integer did) ;
1 2 3 <select id ="getDeptAndEmpByStepTwo" resultType ="Emp" > select * from t_emp where did = #{did} </select >
1 2 3 4 5 6 7 @Test public void test4 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); Dept dept = mapper.getDeptAndEmpByStepOne(1 ); System.out.println(dept); }
执行结果:
8 动态SQL Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题(多条件查询)。
8.1 if标签 if标签可通过test
属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行(拼接到SQL语句中);反之标签中的内容不会执行。
代码示例
1 2 3 4 List<Emp> getEmpByCondition (Emp emp) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="getEmpByCondition" resultType ="Emp" > select * from t_emp where 1 = 1 <if test ="empName != null and empName != ''" > and emp_name = #{empName} </if > <if test ="age != null and age!= ''" > and age = #{age} </if > <if test ="sex != null and sex!= ''" > and sex = #{sex} </if > <if test ="email != null and email!= ''" > and email = #{email} </if > </select >
1 2 3 4 5 6 7 8 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class); Emp emp = new Emp (null , "" , null , "男" , "123@123.com" ); List<Emp> emps = mapper.getEmpByCondition(emp); emps.forEach(System.out::println); }
发现姓名和年龄并不符合条件查询的条件,因此在执行SQL时会去掉这两个条件。
执行结果如下:
可以看到只对符合条件的sex和email进行查询。
8.2 where标签
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="getEmpByCondition2" resultType ="Emp" > select * from t_emp <where > <if test ="empName != null and empName != ''" > and emp_name = #{empName} </if > <if test ="age != null and age!= ''" > and age = #{age} </if > <if test ="sex != null and sex!= ''" > and sex = #{sex} </if > <if test ="email != null and email!= ''" > and email = #{email} </if > </where > </select >
1 2 3 4 5 6 7 8 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class); Emp emp = new Emp (null , "" , 24 , "" , "123@123.com" ); List<Emp> emps = mapper.getEmpByCondition2(emp); emps.forEach(System.out::println); }
执行结果:
1 2 3 4 5 6 7 8 DEBUG 06-06 11:33:18,866 ==> Preparing: select * from t_emp WHERE age = ? and email = ? (BaseJdbcLogger.java:137) DEBUG 06-06 11:33:18,896 ==> Parameters: 24(Integer), 123@123.com(String) (BaseJdbcLogger.java:137) DEBUG 06-06 11:33:18,925 <== Total: 5 (BaseJdbcLogger.java:137) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null) Emp(eid=2, empName=李四, age=24, sex=男, email=123@123.com, dept=null) Emp(eid=3, empName=王五, age=24, sex=男, email=123@123.com, dept=null) Emp(eid=4, empName=赵六, age=24, sex=女, email=123@123.com, dept=null) Emp(eid=5, empName=田七, age=24, sex=男, email=123@123.com, dept=null)
8.3 trim标签
trim用于去掉或添加标签中的内容:
常用属性:
prefix
:在trim标签中的内容的前面添加某些内容
prefixOverrides
:在trim标签中的内容的前面去掉某些内容
suffix
:在trim标签中的内容的后面添加某些内容
suffixOverrides
:在trim标签中的内容的后面去掉某些内容
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="getEmpByCondition3" resultType ="Emp" > select * from t_emp <trim prefix ="where" suffixOverrides ="and|or" > <if test ="empName != null and empName != ''" > emp_name = #{empName} and </if > <if test ="age != null and age!= ''" > age = #{age} or </if > <if test ="sex != null and sex!= ''" > sex = #{sex} and </if > <if test ="email != null and email!= ''" > email = #{email} </if > </trim > </select >
8.4 choose|when|otherwise标签 choose、when、otherwise相当于if...else if..else
代码略
8.5 foreach标签 代码略
8.6 SQL标签 sql片段(标签),可以记录一段公共sql片段,在使用的地方通过include
标签进行引入。
代码示例
1 2 3 4 5 <sql id ="empColumns" > eid,ename,age,sex,did </sql > select <include refid ="empColumns" > </include > from t_emp
9 MyBatis缓存 缓存机制只对查询功能有效。
9.1 一级缓存 一级缓存是SqlSession
级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
代码演示1
1 2 3 4 5 6 7 8 9 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); CacheMapper mapper = sqlSession.getMapper(CacheMapper.class); Emp emp1 = mapper.getEmpByEid(1 ); System.out.println(emp1); Emp emp2 = mapper.getEmpByEid(1 ); System.out.println(emp2); }
执行结果:
1 2 3 4 5 DEBUG 06-06 15:44:32,474 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) DEBUG 06-06 15:44:32,519 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-06 15:44:32,551 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null)
发现只执行了一次sql,原因:第二次是直接从缓存中取得的数据,而不是执行sql从数据库中取得数据。
代码演示2
1 2 3 4 5 6 7 8 9 10 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); CacheMapper mapper1 = sqlSession.getMapper(CacheMapper.class); Emp emp1 = mapper1.getEmpByEid(1 ); System.out.println(emp1); CacheMapper mapper2 = sqlSession.getMapper(CacheMapper.class); Emp emp2 = mapper2.getEmpByEid(1 ); System.out.println(emp2); }
执行结果:
1 2 3 4 5 DEBUG 06-06 15:47:33,176 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) DEBUG 06-06 15:47:33,213 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-06 15:47:33,251 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null)
发现在同一个SQLSession 中查询的数据时会被缓存的。
代码演示3
1 2 3 4 5 6 7 8 9 10 11 @Test public void test0 () { SqlSession sqlSession1 = SqlSessionUtils.getSqlSession(); CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class); Emp emp1 = mapper1.getEmpByEid(1 ); System.out.println(emp1); SqlSession sqlSession2 = SqlSessionUtils.getSqlSession(); CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class); Emp emp2 = mapper2.getEmpByEid(1 ); System.out.println(emp2); }
执行结果:
1 2 3 4 5 6 7 8 DEBUG 06-06 15:49:09,032 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) DEBUG 06-06 15:49:09,076 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-06 15:49:09,118 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null) DEBUG 06-06 15:49:09,247 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) DEBUG 06-06 15:49:09,248 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-06 15:49:09,250 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null)
结论:一级缓存默认开启,且作用域仅限于SqlSession内 。
使一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同(查询到新的数据)
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); CacheMapper mapper1 = sqlSession.getMapper(CacheMapper.class); Emp emp1 = mapper1.getEmpByEid(1 ); System.out.println(emp1); sqlSession.clearCache(); CacheMapper mapper2 = sqlSession.getMapper(CacheMapper.class); Emp emp2 = mapper2.getEmpByEid(1 ); System.out.println(emp2); }
9.2 二级缓存 9.2.1 基本说明和使用 二级缓存是SqlSessionFactory
级别,通过同一个SqlSessionFactory 创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句 ,结果就会从缓存中获取。
代码示例
1 2 3 4 5 6 <mapper namespace ="com.hongyi.mapper.CacheMapper" > <cache /> <select id ="getEmpByEid" resultType ="Emp" > select * from t_emp where eid = #{eid} </select > </mapper >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void test1 () { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); SqlSession sqlSession1 = sqlSessionFactory.openSession(true ); CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class); System.out.println(mapper1.getEmpByEid(1 )); sqlSession1.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(true ); CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class); System.out.println(mapper2.getEmpByEid(1 )); sqlSession2.close(); } catch (IOException e) { e.printStackTrace(); } }
执行结果:
1 2 3 4 5 6 7 DEBUG 06-06 16:12:05,301 Cache Hit Ratio [com.hongyi.mapper.CacheMapper]: 0.0 (LoggingCache.java:60) DEBUG 06-06 16:12:05,872 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) DEBUG 06-06 16:12:05,919 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-06 16:12:05,965 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null) DEBUG 06-06 16:12:05,990 Cache Hit Ratio [com.hongyi.mapper.CacheMapper]: 0.5 (LoggingCache.java:60) Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, dept=null)
发现只执行了一次sql语句。
9.2.2 相关配置 在mapper配置文件中添加的cache标签可以设置一些属性:
9.3 缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之前,数据缓存在一级缓存,SqlSession关闭之后,一级缓存中的数据会写入二级缓存
9.4 整合第三方缓存EHCache 9.4.1 添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency >
9.4.2 配置
创建EHCache的配置文件ehcache.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="utf-8" ?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="../config/ehcache.xsd" > <diskStore path ="D:\atguigu\ehcache" /> <defaultCache maxElementsInMemory ="1000" maxElementsOnDisk ="10000000" eternal ="false" overflowToDisk ="true" timeToIdleSeconds ="120" timeToLiveSeconds ="120" diskExpiryThreadIntervalSeconds ="120" memoryStoreEvictionPolicy ="LRU" > </defaultCache > </ehcache >
1 <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
创建logback的配置文件logback.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?xml version="1.0" encoding="UTF-8" ?> <configuration debug ="true" > <appender name ="STDOUT" class ="ch.qos.logback.core.ConsoleAppender" > <encoder > <pattern > [%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n </pattern > </encoder > </appender > <root level ="DEBUG" > <appender-ref ref ="STDOUT" /> </root > <logger name ="com.atguigu.crowd.mapper" level ="DEBUG" /> </configuration >
10 MyBatis逆向工程
10.1 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.7</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.0</version > <dependencies > <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.3.2</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.8</version > </dependency > </dependencies > </plugin > </plugins > </build >
创建mybatis核心配置文件
创建逆向工程的配置文件
文件名必须是:generatorConfig.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <context id ="DB2Tables" targetRuntime ="MyBatis3Simple" > <jdbcConnection driverClass ="com.mysql.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/test" userId ="root" password ="12345678" > </jdbcConnection > <javaModelGenerator targetPackage ="com.hongyi.pojo" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.hongyi.mapper" targetProject =".\src\main\resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.hongyi.mapper" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="t_emp" domainObjectName ="Emp" /> <table tableName ="t_dept" domainObjectName ="Dept" /> </context > </generatorConfiguration >
执行结果
双击下图所示按钮:
即可生成:
10.2 QBC查询 略
11 分页 11.1 配置
1 2 3 4 5 6 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.2.0</version > </dependency >
配置分页插件:在MyBatis的核心配置文件中配置插件
1 2 3 4 <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" /> </plugins >
11.2 使用
在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)
开启分页功能
pageNum:当前页的页码
pageSize:每页显示的条数
在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)
获取分页相关数据
list
:分页之后的数据
navigatePages
:导航分页的页码数,例如值为3,则展示第2页时显示的为1 2 3
分页相关数据:
pageNum
:当前页的页码
pageSize
:每页显示的条数
size
:当前页显示的真实条数
total
:总记录数
pages
:总页数
prePage
:上一页的页码
nextPage
:下一页的页码
isFirstPage/isLastPage
:是否为第一页/最后一页
hasPreviousPage/hasNextPage
:是否存在上一页/下一页
navigatePages
:导航分页的页码数
navigatepageNums
:导航分页的页码,[1,2,3,4,5]
代码示例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); PageHelper.startPage(1 , 4 ); List<Emp> list = mapper.selectByExample(null ); list.forEach(System.out::println); }
代码示例2——获取分页对象(方法1)
1 2 3 4 5 6 7 8 9 10 @Test public void test0 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Page<Object> page = PageHelper.startPage(1 , 4 ); System.out.println(page); List<Emp> list = mapper.selectByExample(null ); list.forEach(System.out::println); }
打印结果:
1 Page{count=true, pageNum=1, pageSize=4, startRow=0, endRow=4, total=0, pages=0, reasonable=null, pageSizeZero=null}[]
代码示例3——获取分页对象(方法2)
1 2 3 4 5 6 7 8 9 10 @Test public void test1 () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); PageHelper.startPage(1 , 4 ); List<Emp> list = mapper.selectByExample(null ); PageInfo<Emp> page = new PageInfo <>(list, 5 ); System.out.println(page); list.forEach(System.out::println); }
打印结果:
1 PageInfo{pageNum=1, pageSize=4, size=4, startRow=1, endRow=4, total=14, pages=4, list=Page{count=true, pageNum=1, pageSize=4, startRow=0, endRow=4, total=14, pages=4, reasonable=false, pageSizeZero=false}[Emp(eid=1, empName=张三, age=24, sex=男, email=123@123.com, did=1), Emp(eid=2, empName=李四, age=24, sex=男, email=123@123.com, did=1), Emp(eid=3, empName=王五, age=24, sex=男, email=123@123.com, did=2), Emp(eid=4, empName=赵六, age=24, sex=女, email=123@123.com, did=2)], prePage=0, nextPage=2, isFirstPage=true, isLastPage=false, hasPreviousPage=false, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=4, navigatepageNums=[1, 2, 3, 4]}
12 原理简介 整体流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test0 () throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int result = mapper.insertUser(); System.out.println("result: " + result); }
12.1 SqlSessionFactoryBuilder SqlSessionFactoryBuilder
是MyBatis中用于创建SqlSessionFactory
的构建器,它负责解析mybatis-config.xml
配置文件中的配置信息,并创建出SqlSessionFactory
对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public SqlSessionFactory build (Reader reader, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder (reader, environment, properties); var5 = this .build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession." , var14); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException var13) { } } return var5; }
其中,又调用了build
的重载方法(第8行var5 = this.build(parser.parse());
):
1 2 3 public SqlSessionFactory build (Configuration config) { return new DefaultSqlSessionFactory (config); }
DefaultSqlSessionFactory
即是接口SqlSessionFactory
的实现类:
1 2 3 4 5 6 7 8 public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory (Configuration configuration) { this .configuration = configuration; } }
12.2 SqlSessionFactory SqlSessionFactory接口是线程安全的,SqlSessionFactory一旦被创建,应该在应用执行期间都存在。在应用运行期间不要重复创建多次,建议使用单例模式。
SqlSessionFactory是创建SqlSession的工厂。源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 public interface SqlSessionFactory { SqlSession openSession () ; SqlSession openSession (boolean var1) ; SqlSession openSession (Connection var1) ; SqlSession openSession (TransactionIsolationLevel var1) ; SqlSession openSession (ExecutorType var1) ; SqlSession openSession (ExecutorType var1, boolean var2) ; SqlSession openSession (ExecutorType var1, TransactionIsolationLevel var2) ; SqlSession openSession (ExecutorType var1, Connection var2) ; Configuration getConfiguration () ; }
调用openSession
,返回SqlSession
对象(SqlSession
为接口,DefaultSqlSession
为其实现类):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private SqlSession openSessionFromDataSource (ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null ; DefaultSqlSession var8; try { Environment environment = this .configuration.getEnvironment(); TransactionFactory transactionFactory = this .getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); Executor executor = this .configuration.newExecutor(tx, execType); var8 = new DefaultSqlSession (this .configuration, executor, autoCommit); } catch (Exception var12) { this .closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); } finally { ErrorContext.instance().reset(); } return var8; }
12.3 SqlSession SqlSession是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection。实现类为DefaultSqlSession
使用完SqlSession之后关闭Session很重要,应该确保使用finally块来关闭它。
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public interface SqlSession extends Closeable { <T> T selectOne (String var1) ; <T> T selectOne (String var1, Object var2) ; <E> List<E> selectList (String var1) ; <E> List<E> selectList (String var1, Object var2) ; <E> List<E> selectList (String var1, Object var2, RowBounds var3) ; <K, V> Map<K, V> selectMap (String var1, String var2) ; <K, V> Map<K, V> selectMap (String var1, Object var2, String var3) ; <K, V> Map<K, V> selectMap (String var1, Object var2, String var3, RowBounds var4) ; <T> Cursor<T> selectCursor (String var1) ; <T> Cursor<T> selectCursor (String var1, Object var2) ; <T> Cursor<T> selectCursor (String var1, Object var2, RowBounds var3) ; void select (String var1, Object var2, ResultHandler var3) ; void select (String var1, ResultHandler var2) ; void select (String var1, Object var2, RowBounds var3, ResultHandler var4) ; int insert (String var1) ; int insert (String var1, Object var2) ; int update (String var1) ; int update (String var1, Object var2) ; int delete (String var1) ; int delete (String var1, Object var2) ; void commit () ; void commit (boolean var1) ; void rollback () ; void rollback (boolean var1) ; List<BatchResult> flushStatements () ; void close () ; void clearCache () ; Configuration getConfiguration () ; <T> T getMapper (Class<T> var1) ; Connection getConnection () ; }
关键代码:
1 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
思想:通过JDK的动态代理 来实现,getMapper
返回的即一个代理对象。