MyBatis笔记 1.简介 MyBatis是什么 MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架 。 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 MyBatis 可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO( Plain Old Java Objects,普通的Java对象)映射成数据库中的记录 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) 为什么要使用MyBatis? JDBCSQL夹在Java代码块里,耦合度高导致硬编码内伤 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见
Hibernate和JPA – 长难复杂SQL,对于Hibernate而言处理也不容易 – 内部自动生产的SQL,不容易做特殊优化。 – 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降。
MyBatis是一个半自动化的持久化层框架。对开发人员而言,核心sql还是需要自己优化 sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。
2.下载 MyBatis下载
MyBatis官网
3.HelloWorld 在MySQL数据库创建一数据库实例learnmybatis
,在其创建一张表 1 2 3 4 5 6 CREATE TABLE employee( id INT (11 ) PRIMARY KEY AUTO_INCREMENT, last_name VARCHAR (255 ), gender CHAR (1 ), email VARCHAR (255 ) );
再插进一条随意数据,用于测试
pom.xml
1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.4.1</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency >
Employee.java
1 2 3 4 5 6 7 8 9 public class Employee { private Integer id; private String lastName; private String email; private String gender; }
创建mybatis配置文件,sql映射文件MyBatis 的全局配置文件包含了影响 MyBatis 行为甚深的设置( settings)和属性( properties)信息、如数据库连接池信息等。指导着MyBatis进行工作。我们可以参照官方文件的配置示例。 映射文件的作用就相当于是定义Dao接口的实现类如何工作。这也是我们使用MyBatis时编写的最多的文件 。 mybatis全局配置文件
mybatis-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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/learnmybatis?useUnicode=true& characterEncoding=UTF-8& serverTimezone=CTT" /> <property name ="username" value ="root" /> <property name ="password" value ="123" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="c01/EmployeeMapper.xml" /> </mappers > </configuration >
sql映射文件
EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <mapper namespace ="abc" > <select id ="getEmpById" resultType ="com.lun.c01.helloworld.bean.Employee" > select id,last_name lastName,email,gender from employee where id = #{id} </select > </mapper >
HelloWorldTest.java
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 public class HelloWorldTest { public SqlSessionFactory getSqlSessionFactory () throws IOException { String resource = "c01/mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); return new SqlSessionFactoryBuilder ().build(inputStream); } @Test public void test () throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { Employee employee = openSession.selectOne( "abc.getEmpById" , 1 ); System.out.println(employee); } finally { openSession.close(); } } }
4.接口式编程 日后主要用接口式编程
HelloWorld-接口式编程
EmployeeMapper.java
1 2 3 4 5 6 7 import com.lun.c01.helloworld.bean.Employee;public interface EmployeeMapper { public Employee getEmpById (Integer id) ; }
修改Mapper文件(命名空间,id,returnType) EmployeeMapper2.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <mapper namespace ="com.lun.c01.helloworld.dao.EmployeeMapper" > <select id ="getEmpById" resultType ="com.lun.c01.helloworld.bean.Employee" > select id,last_name lastName,email,gender from employee where id = #{id} </select > </mapper >
HelloWorldTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test01 () throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee employee = mapper.getEmpById(1 ); System.out.println(mapper.getClass()); System.out.println(employee); } finally { openSession.close(); } }
5.小结(1) 接口式编程原生: Dao ====> DaoImpl mybatis: Mapper ====> xxMapper.xml SqlSession代表和数据库的一次会话;用完必须关闭; SqlSession和connection一样她都是非线程安全。每次使用都应该去获取新的对象。 mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。 (将接口和xml进行绑定)EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class);
两个重要的配置文件:mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等…系统运行环境信息 sql映射文件:保存了每一个sql语句的映射信息:将sql抽取出来。 6.全局配置文件-引入dtd约束 MyBatis-全局配置文件 MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置( settings)和属性( properties)信息。文档的顶层结构如下:
configuration 配置properties 属性 settings 设置 typeAliases 类型命名 typeHandlers 类型处理器 objectFactory 对象工厂 plugins 插件 environments 环境environment 环境变量transactionManager 事务管理器 dataSource 数据源 databaseIdProvider 数据库厂商标识 mappers 映射器 引入dtd约束 有时Eclipse在编辑全局xml或映射xml时没有编辑提示,这时可手动导入dtd,导入后才有编辑提示
dtd文件在Mybatis的Jar包的org/apache/ibatis/builder/xml,导入前需Mybatis的Jar包中的dtd存放到本地目录
设置路径:Window -> Preferences -> XML -> XML Catalog -> Add Catalog Entry ->Set URL key and DTD local location
7.全局配置文件-properties-引入外部配置文件 官方文档
mybatis-config.xml
1 2 3 4 5 6 7 8 <configuration > <properties resource ="dbconfig.properties" > </properties >
dbconfig.properties
1 2 3 4 jdbc.driver =com.mysql.cj.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/learnmybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT jdbc.username =root jdbc.password =123
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
在 properties 元素体内指定的属性首先被读取。 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根 据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。 8.全局配置文件-settings-运行时行为设置 这是 MyBatis 中极为重要的调整设置,它们会改变MyBatis 的运行时行为。
mybatis-config2.xml
1 2 3 4 5 6 7 8 9 10 11 12 <configuration > ... <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
设置参数 描述 有效值 默认值 cacheEnabled 该配置影响的所有映射器中配置的缓存的全局开关。 true/false TRUE lazyLoadingEnabled 延迟加载的全局开关。当开启时。所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 true/false FALSE useColumnLabel 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 true/false TRUE defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数。 Any positive integer Not Set (null) mapUnderscoreToCamelCase 是否开启自动驼峰命名规则( camel case )映射即从经典数据库列名A_ COLUMN到经典Java属性名aColumn的类似映射 true/false FALSE
更多设置
9.全局配置文件-typeAliases-别名 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
1 2 3 4 5 6 7 8 9 10 <configuration > ... <typeAliases > <typeAlias alias ="Author" type ="domain.blog.Author" /> <typeAlias alias ="Blog" type ="domain.blog.Blog" /> <typeAlias alias ="Comment" type ="domain.blog.Comment" /> <typeAlias alias ="Post" type ="domain.blog.Post" /> <typeAlias alias ="Section" type ="domain.blog.Section" /> <typeAlias alias ="Tag" type ="domain.blog.Tag" /> </typeAliases >
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
1 2 3 4 5 <configuration > ... <typeAliases > <package name ="domain.blog" /> </typeAliases >
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
1 2 3 4 @Alias("author") public class Author { ... }
值得注意的是, MyBatis已经为许多常见的 Java 类型内建 了相应的类型别名。它们都是大小写不敏感的 ,我们在起 别名的时候千万不要占用已有的别名。
别名 映射的类型 别名 映射的类型 别名 映射的类型 _byte byte string String date Date _long long byte Byte decimal BigDecimal _short short long Long bigdecimal BigDecimal _int int short Short object Object _integer int int Integer map Map _double double integer Integer hashmap HashMap _float float double Double list List _boolean boolean float Float arraylist ArrayList - - boolean Boolean collection Collection - - - - iterator Iterator
mybatis-config3.xml
10.全局配置文件-typeHandlers-类型处理器简介 官方文档
无论是 MyBatis 在预处理语句( PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
类型处理器 Java 类型 JDBC 类型 BooleanTypeHandler java.lang.Boolean, boolean 数据库兼容的 BOOLEAN ByteTypeHandler java.lang.Byte, byte 数据库兼容的 NUMERIC 或 BYTE ShortTypeHandler java.lang.Short, short 数据库兼容的 NUMERIC 或 SHORT INTEGER IntegerTypeHandler java.lang.Integer, int 数据库兼容的 NUMERIC 或 INTEGER LongTypeHandler java.lang.Long, long 数据库兼容的 NUMERIC 或 LONG INTEGER FloatTypeHandler java.lang.Float, float 数据库兼容的 NUMERIC 或 FLOAT DoubleTypeHandler java.lang.Double, double 数据库兼容的 NUMERIC 或 DOUBLE BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL StringTypeHandler java.lang.String CHAR, VARCHAR
more
日期类型的处理 日期和时间的处理, JDK1.8以前一直是个头疼的问题。我们通常使用JSR310规范领导者Stephen Colebourne创建的Joda-Time来操作。 1.8已经实现全部的JSR310规范了。 日期时间处理上,我们可以使用MyBatis基于JSR310( Date and Time API)编写的各种日期时间类型处理器。 MyBatis3.4以前的版本需要我们手动注册这些处理器,以后的版本都是自动注册的 1 2 3 4 5 6 7 8 9 10 11 12 13 <typeHandlers > <typeHandler handler ="org.apache.ibatis.type.InstantTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.LocalDateTimeTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.LocalDateTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.LocalTime TypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.0ffsetDateTimeTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.OffsetTimeTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.ZonedDateTimeTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.YearTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.MonthTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.YearMonthTypeHandler" /> <typeHandler handler ="org.apache.ibatis.type.JapaneseDateTypeHandler" /> </typeHandlers >
自定义类型处理器 自定义类型处理器示例
我们可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。
步骤:
实现org.apache.ibatis.type.TypeHandler
接口或者继承org.apache.ibatis.type.BaseTypeHandler
指定其映射某个JDBC类型(可选操作) 在mybatis全局配置文件中注册 11.全局配置文件-plugins-插件简介 官方文档
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。 插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。
了解mybatis运行原理才能更好开发插件。
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
12.全局配置文件-enviroments-运行环境 enviroments - 官方文档
MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。 每种环境使用一个environment标签进行配置并指定唯一标识符 可以通过environments标签中的default属性指定一个环境的标识符来快速的切换环境 environment-指定具体环境 id:指定当前环境的唯一标识 transactionManager、和dataSource都必须有 1 2 3 4 5 6 7 8 9 10 11 <environments default ="dev_mysql" > <environment id ="dev_mysql" > <transactionManager type ="JDBC" > </transactionManager > <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 > ...
transactionManager type: JDBC | MANAGED | 自定义JDBC:使用了 JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围。JdbcTransactionFactory MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 ManagedTransactionFactory 自定义:实现TransactionFactory接口, type=全类名/别名 dataSource type: UNPOOLED | POOLED | JNDI | 自定义UNPOOLED:不使用连接池, UnpooledDataSourceFactory POOLED:使用连接池, PooledDataSourceFactory JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源 自定义:实现DataSourceFactory接口,定义数据源的获取方式。 实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置 。
13.全局配置文件-databaseIdProvider-多数据库支持 databaseIdProvider - 官方文档
MyBatis 可以根据不同的数据库厂商执行不同的语句。 1 2 3 4 5 6 <databaseIdProvider type ="DB_VENDOR" > <property name ="SQL Server" value ="sqlserver" /> <property name ="DB2" value ="db2" /> <property name ="Oracle" value ="oracle" /> <property name ="MySQL" value ="mysql" /> </databaseIdProvider >
Type: DB_VENDOR 使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义。 Property-name:数据库厂商标识 Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用 DB_VENDOR - 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置。由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短
1 2 3 4 5 6 7 8 <select id ="getEmpById" resultType ="com.lun.c01.helloworld.bean.Employee" databaseId ="mysql" > select * from employee where id = #{id} </select > <select id ="getEmpById" resultType ="com.lun.c01.helloworld.bean.Employee" databaseId ="oracle" > select e.* from employee e where id = #{id} </select >
通过<environments default="mysql">
切换数据库,便能切换SQL MyBatis匹配规则如下:
如果没有配置databaseIdProvider标签,那么databaseId=null 如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上设置databaseId=配置指定的值,否则依旧为null 如果databaseId不为null,他只会找到配置databaseId的sql语句 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带 databaseId 的相同语句, 则后者会被舍弃。 14.全局配置文件-mappers-sql映射注册 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件 。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:
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 <mappers > <mapper resource ="org/mybatis/builder/AuthorMapper.xml" /> <mapper resource ="org/mybatis/builder/BlogMapper.xml" /> <mapper resource ="org/mybatis/builder/PostMapper.xml" /> </mappers > <mappers > <mapper url ="file:///var/mappers/AuthorMapper.xml" /> <mapper url ="file:///var/mappers/BlogMapper.xml" /> <mapper url ="file:///var/mappers/PostMapper.xml" /> </mappers > <mappers > <mapper class ="org.mybatis.builder.AuthorMapper" /> <mapper class ="org.mybatis.builder.BlogMapper" /> <mapper class ="org.mybatis.builder.PostMapper" /> </mappers > <mappers > <package name ="org.mybatis.builder" /> </mappers >
这些配置会告诉 MyBatis 去哪里找映射文件
15.小结(2) 注意mybatis全局配置文件中的标签顺序,需要按以下顺序排列,否则抛异常
configuration 配置properties 属性 settings 设置 typeAliases 类型命名 typeHandlers 类型处理器 objectFactory 对象工厂 plugins 插件 environments 环境environment 环境变量transactionManager 事务管理器 dataSource 数据源 databaseIdProvider 数据库厂商标识 mappers 映射器 16.映射文件-增删改查 映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义;
cache –命名空间的二级缓存配置 cache-ref – 其他命名空间缓存配置的引用。 resultMap – 自定义结果集映射 parameterMap – 已废弃 !老式风格的参数映射 sql –抽取可重用语句块。 insert – 映射插入语句 update – 映射更新语句 delete – 映射删除语句 select – 映射查询语句 CRUD初体验 EmployeeMapper.java
1 2 3 4 5 6 7 8 9 10 11 public interface EmployeeMapper { public Employee getEmpById (Integer id) ; public Long addEmp (Employee employee) ; public boolean updateEmp (Employee employee) ; public void deleteEmpById (Integer id) ; }
EmployeeMapper.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 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <select id ="getEmpById" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where id = #{id} </select > <insert id ="addEmp" parameterType ="com.lun.c01.helloworld.bean.Employee" useGeneratedKeys ="true" keyProperty ="id" > insert into employee(last_name,email,gender) values(#{lastName},#{email},#{gender}) </insert > <update id ="updateEmp" > update employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id} </update > <delete id ="deleteEmpById" > delete from employee where id=#{id} </delete > </mapper >
EmployeeMapperTest.java
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 public class MapperTest { @Test public void testCrud () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); Employee newEmployee = new Employee (null ,"kuang" ,"kuang@163.com" ,"1" ); Long count = mapper.addEmp(newEmployee); System.out.println("After creating : " + mapper.getEmpById(newEmployee.getId())); newEmployee.setGender("0" ); mapper.updateEmp(newEmployee); System.out.println("After updating : " + mapper.getEmpById(newEmployee.getId())); mapper.deleteEmpById(newEmployee.getId()); System.out.println("After deleting : " + mapper.getEmpById(newEmployee.getId())); session.commit(); } finally { session.close(); } } }
另外 mybatis允许增删改直接定义以下类型返回值Integer、Long、Boolean、void 我们需要手动提交数据sqlSessionFactory.openSession();===》手动提交 sqlSessionFactory.openSession(true);===》自动提交 17.映射文件-insert-获取自增主键的值 parameterType:参数类型,可以省略, 获取自增主键的值:mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys(); useGeneratedKeys=”true”;使用自增主键获取主键值策略 keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性 EmployeeMapper.xml
1 2 3 4 5 6 <insert id ="addEmp" parameterType ="com.lun.c01.helloworld.bean.Employee" useGeneratedKeys ="true" keyProperty ="id" > insert into employee(last_name,email,gender) values(#{lastName},#{email},#{gender}) </insert >
18.映射文件-insert-Oracle使用序列生成主键演示 Oracle不支持自增;Oracle使用序列来模拟自增; 每次插入的数据的主键是从序列中拿到的值;如何获取到这个值; 1 2 #从序列获取新主键值 select employee_seq.nextval from dual;
19.(oracle使用)映射文件-insert-获取非自增主键的值-selectKey 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 <insert id ="addEmp" databaseId ="oracle" > <selectKey keyProperty ="id" order ="BEFORE" resultType ="Integer" > select EMPLOYEES_SEQ.nextval from dual </selectKey > insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) values(#{id},#{lastName},#{email}) </insert >
selectKey 元素的属性
属性 描述 keyProperty selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。 keyColumn 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。 resultType 结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。 order 可以设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 statementType 和前面一样,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 类型的映射语句,分别代表 Statement, PreparedStatement 和 CallableStatement 类型。
20.映射文件-参数处理-单个参数&多个参数&命名参数 单个参数:mybatis不会做特殊处理, 多个参数:mybatis会做特殊处理。通常操作:方法:public Employee getEmpByIdAndLastName(Integer id,String lastName); 取值:#{id}
,#{lastName}
上述操作会抛出异常:org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2]
多个参数会被封装成 一个map,key:param1…paramN,或者参数的索引也可以 value:传入的参数值 #{}
就是从map中获取指定的key的值; 【命名参数】:明确指定封装参数时map的key;@Param(“id”)多个参数会被封装成 一个map,key:使用@Param注解指定的值 value:参数值 #{指定的key}
取出对应的参数值 EmployeeMapperTest.java
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 @Test public void testParameters () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); System.out.println(mapper.getEmpById2(1 )); try { System.out.println(mapper.getEmpByIdAndLastName(1 , "jallen" )); }catch (PersistenceException ex) { System.err.println(ex); } System.out.println(mapper.getEmpByIdAndLastName2(1 , "jallen" )); System.out.println(mapper.getEmpByIdAndLastName3(1 , "jallen" )); System.out.println(mapper.getEmpByIdAndLastName4(1 , "jallen" )); session.commit(); } finally { session.close(); } }
EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <select id ="getEmpByIdAndLastName" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from tbl_employee where id = #{id} and last_name=#{lastName} </select > <select id ="getEmpByIdAndLastName2" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where id = #{0} and last_name=#{1} </select > <select id ="getEmpByIdAndLastName3" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where id = #{param1} and last_name=#{param2} </select > <select id ="getEmpByIdAndLastName4" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where id = #{id} and last_name=#{lastName} </select > ...
EmployeeMapper.java
1 2 3 4 5 6 7 8 public interface EmployeeMapper { public Employee getEmpByIdAndLastName (Integer id, String name) ; public Employee getEmpByIdAndLastName2 (Integer id, String name) ; public Employee getEmpByIdAndLastName3 (Integer id, String name) ; public Employee getEmpByIdAndLastName4 (@Param("id") Integer id,// @Param("lastName") String name) ; ...
21.映射文件-参数处理-POJO&Map&TO POJO:如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo; Map:如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map TO:如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象,如: 1 2 3 4 Page{ int index; int size; }
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void testParameters () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); ... Map<String, Object> map = new HashMap <>(); map.put("id" , 1 ); map.put("lastName" , "jallen" ); System.out.println("4. " + mapper.getEmpByMap(map)); session.commit(); } finally { session.close(); } }
EmployeeMapper.xml
1 2 3 4 5 6 7 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <select id ="getEmpByMap" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where id = #{id} and last_name=#{lastName} </select > ...
EmployeeMapper.java
1 2 3 public interface EmployeeMapper { public Employee getEmpByMap (Map<String, Object> map) ; ...
22.映射文件-参数处理-参数封装扩展思考 ========================思考================================
public Employee getEmp(@Param("id")Integer id,String lastName);
取值:id==>#{id/param1} lastName==>#{param2} public Employee getEmp(Integer id,@Param("e")Employee emp);
取值:id==>#{param1} lastName===>#{param2.lastName/e.lastName} 特别注意 :如果是Collection(List、Set)类型或者是数组,
也会特殊处理。也是把传入的list或者数组封装在map中。 key:Collection(collection),如果是List还可以使用这个key(list) public Employee getEmpById(List<Integer> ids);
23.源码分析-参数处理-参数封装map的过程 结合源码,mybatis怎么处理参数
(@Param(“id”)Integer id,@Param(“lastName”)String lastName); ParamNameResolver解析参数封装map的; names:{0=id, 1=lastName};构造器的时候就确定好了 确定流程: 1.获取每个标了param注解的参数的@Param的值:id,lastName; 赋值给name; 2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值) name的值: 标注了param注解:注解的值 没有标注: 1.全局配置:useActualParamName(jdk1.8):name=参数名 2.name=map.size();相当于当前元素的索引 {0=id, 1=lastName,2=2}
args【1,”Tom”,’hello’】:
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 public Object getNamedParams (Object[] args) { final int paramCount = names.size(); if (args == null || paramCount == 0 ) { return null ; } else if (!hasParamAnnotation && paramCount == 1 ) { return args[names.firstKey()]; } else { final Map<String, Object> param = new ParamMap <Object>(); int i = 0 ; for (Map.Entry<Integer, String> entry : names.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1 ); if (!names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } } }
总结 :参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;#{key}就可以取出map中的值;
24.映射文件-参数处理-#与$取值区别 #{}
和${}
都可以获取map中的值或者pojo对象属性的值;
1 2 3 select * from tbl_employee where id= ${id} and last_name= #{lastName}#Preparing: select * from tbl_employee where id= 2 and last_name= ?
区别 :
#{}
: 是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入${}
: 取出的值直接拼装 在sql语句中;会有安全问题;大多情况下,我们去参数的值都应该去使用#{}
。
原生jdbc不支持占位符的地方我们就可以使用${}
进行取值,比如分表、排序。。。;按照年份分表拆分
1 2 select * from ${year }_salary where xxx;select * from tbl_employee order by ${f_name} ${order }
25.映射文件-参数处理-#取值时指定参数相关规则 #{}
:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、 resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能); 例如:jdbcType
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle DB(报错); JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,Oracle DB不能正确处理; 由于全局配置中:jdbcTypeForNull=OTHER,Oracle DB不支持,两种解决方法:
在mapper文件中写#{email,jdbcType=NULL}
; 在全局配置文件<setting name="jdbcTypeForNull" value="NULL"/>
26.映射文件-select-返回List EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <select id ="getEmpsByLastNameLike" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where last_name like #{lastName} </select > ...
EmployeeMapper.java
1 2 3 4 5 public interface EmployeeMapper { public List<Employee> getEmpsByLastNameLike (String str) ; ...
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testList () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); List<Employee> result = em.getEmpsByLastNameLike("%a%" ); System.out.println(result); session.commit(); } finally { session.close(); } }
27.映射文件-select-记录封装map EmployeeMapper.java
1 2 3 4 5 6 7 8 9 10 11 public interface EmployeeMapper { @MapKey("lastName") public Map<String, Employee> getEmpByLastNameLikeReturnMap (String lastName) ; public Map<String, Object> getEmpByIdReturnMap (Integer id) ; ...
EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <select id ="getEmpByLastNameLikeReturnMap" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where last_name like #{lastName} </select > <select id ="getEmpByIdReturnMap" resultType ="map" > select * from employee where id=#{id} </select > ...
MapperTest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void testMap () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); Map<String, Object> result = em.getEmpByIdReturnMap(1 ); System.out.println(result); System.out.println("---" ); Map<String, Employee> result2 = em.getEmpByLastNameLikeReturnMap("%a%" ); System.out.println(result2); session.commit(); } finally { session.close(); } }
28.映射文件-select-resultMap-自定义结果映射规则 EmployeeMapper.java
1 2 3 4 5 6 public interface EmployeeMapper { public Employee getEmpByIdWithResultMap (Integer id) ; ...
EmployeeMapper.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 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Employee" id ="MySimpleEmp" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="email" property ="email" /> <result column ="gender" property ="gender" /> </resultMap > <select id ="getEmpByIdWithResultMap" resultMap ="MySimpleEmp" > select * from employee where id=#{id} </select >
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testResultMap () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); System.out.println(em.getEmpByIdWithResultMap(1 )); session.commit(); } finally { session.close(); } }
29.映射文件-select-resultMap-关联查询-环境搭建 新建类Department.java
1 2 3 4 5 6 7 8 public class Department { private Integer id; private String departmentName; private List<Employee> emps;
修改类Employee.java
1 2 3 4 5 6 7 8 9 public class Employee { ... private Department department; ... public Employee () {}
创建数据库表
1 2 3 4 5 6 7 8 9 10 11 12 CREATE TABLE department( id int (11 ) primary key auto_increment, department_name varchar (255 ) ); ALTER TABLE employee ADD COLUMN department_id int (11 );ALTER TABLE employee ADD CONSTRAINT fk_employee_department FOREIGN KEY(department_id) REFERENCES department(id);INSERT INTO department(department_name) values ('开发部' );INSERT INTO department(department_name) values ('测试部' );
30.映射文件-select-resultMap-关联查询-级联属性封装结果 EmployeeMapper.java
1 2 3 4 5 6 public interface EmployeeMapper { public Employee getEmpAndDept (Integer id) ; ...
EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Employee" id ="MyDifEmp" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="department_id" property ="department.id" /> <result column ="department_name" property ="department.departmentName" /> </resultMap > <select id ="getEmpAndDept" resultMap ="MyDifEmp" > SELECT e.id id,e.last_name last_name,e.gender gender, e.department_id department_id, d.department_name department_name FROM employee e, department d WHERE e.department_id=d.id AND e.id=#{id} </select > ...
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testResultMapAssociation () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); System.out.println(em.getEmpAndDept(1 )); session.commit(); } finally { session.close(); } }
31.映射文件-select-resultMap-关联查询-association定义关联对象封装规则 EmployeeMapper.java
1 2 3 4 5 public interface EmployeeMapper { public Employee getEmpAndDept2 (Integer id) ;
注意:使用该方法不写的column和property不会自动封装,因此必须写全,不能偷懒!
EmployeeMapper.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 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Employee" id ="MyDifEmp2" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <association property ="department" javaType ="com.lun.c01.helloworld.bean.Department" > <id column ="department_id" property ="id" /> <result column ="department_name" property ="departmentName" /> </association > </resultMap > <select id ="getEmpAndDept2" resultMap ="MyDifEmp2" > SELECT e.id id,e.last_name last_name,e.gender gender, e.department_id department_id, d.department_name department_name FROM employee e, department d WHERE e.department_id=d.id AND e.id=#{id} </select >
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void testResultMapAssociation2 () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); System.out.println(em.getEmpAndDept2(1 )); System.out.println(em.getEmpAndDept2(1 ).getDepartment()); session.commit(); } finally { session.close(); } }
32.映射文件-select-resultMap-关联查询-association分步查询 DepartmentMapper.java
1 2 3 4 public interface DepartmentMapper { public Department getDeptById (Integer id) ; }
DepartmentMapper.xml
1 2 3 4 5 6 <mapper namespace ="com.lun.c03.mapper.dao.DepartmentMapper" > <select id ="getDeptById" resultType ="com.lun.c01.helloworld.bean.Department" > select id,department_name departmentName from department where id=#{id} </select > </mapper >
mybatis-config.xml
1 2 3 4 5 6 7 ... <mappers > <mapper resource ="c03/EmployeeMapper.xml" /> <mapper resource ="c03/DepartmentMapper.xml" /> </mappers > </configuration >
EmployeeMapper.java
1 2 3 4 5 public interface EmployeeMapper { public Employee getEmpByIdStep (Integer id) ; ...
EmployeeMapper.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 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Employee" id ="MyEmpByStep" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="email" property ="email" /> <result column ="gender" property ="gender" /> <association property ="department" select ="com.lun.c03.mapper.dao.DepartmentMapper.getDeptById" column ="department_id" > </association > </resultMap > <select id ="getEmpByIdStep" resultMap ="MyEmpByStep" > select * from employee where id=#{id} </select >
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void testResultMapAssociation3 () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); System.out.println(em.getEmpByIdStep(1 )); System.out.println(em.getEmpByIdStep(1 ).getDepartment()); session.commit(); } finally { session.close(); } }
33.映射文件-select-resultMap-关联查询-分步查询&延迟加载 我们每次查询Employee对象的时候,都将一起查询出来。部门信息在我们使用的时候再去查询;分段查询的基础之上加上两个配置:
在全局配置文件中配置,实现懒加载
mybatis-config.xml
1 2 3 4 5 6 7 8 <configuration > ... <settings > ... <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings >
Setting Description Valid Values Default lazyLoadingEnabled Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be superseded for an specific relation by using the fetchType attribute on it. true/false false aggressiveLazyLoading When enabled, an object with lazy loaded properties will be loaded entirely upon a call to any of the lazy properties. Otherwise, each property is loaded on demand. true/false true
aggressive 英 [əˈɡresɪv] 美 [əˈɡresɪv] adj. 好斗的;挑衅的;侵略的;富于攻击性的;气势汹汹的;声势浩大的;志在必得的
PS. 个人认为aggressiveLazyLoading 可防止懒加载对象链 情况出现。TODO: 待验证
34.映射文件-select-resultMap-关联查询-collection定义关联集合封装规则 DepartmentMapper.java
1 2 3 public interface DepartmentMapper { public Department getDeptByIdPlus (Integer id) ; ...
DepartmentMapper.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 <mapper namespace ="com.lun.c03.mapper.dao.DepartmentMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Department" id ="MyDept" > <id column ="did" property ="id" /> <result column ="department_name" property ="departmentName" /> <collection property ="emps" ofType ="com.lun.c01.helloworld.bean.Employee" > <id column ="eid" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="email" property ="email" /> <result column ="gender" property ="gender" /> </collection > </resultMap > <select id ="getDeptByIdPlus" resultMap ="MyDept" > SELECT d.id did,d.department_name department_name, e.id eid,e.last_name last_name, e.email email,e.gender gender FROM department d LEFT JOIN employee e ON d.id=e.department_id WHERE d.id=#{id} </select > ...
DepartmentMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class DepartmentTest { @Test public void testGetDeptByIdPlus () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DepartmentMapper dm = session.getMapper(DepartmentMapper.class); Department department = dm.getDeptByIdPlus(1 ); System.out.println(department); System.out.println(department.getEmps()); session.commit(); } finally { session.close(); } } }
35.映射文件-select-resultMap-关联查询-collection分步查询&延迟加载 EmployeeMapper.java
1 2 3 public interface EmployeeMapper { public Employee getEmpsByDeptId (Integer id) ; ...
EmployeeMapper.xml
1 2 3 4 5 6 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <select id ="getEmpsByDeptId" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where department_id=#{department_id} </select >
DepartmentMapper.java
1 2 3 public interface DepartmentMapper { public Department getDeptByIdStep (Integer id) ; ...
DepartmentMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <mapper namespace ="com.lun.c03.mapper.dao.DepartmentMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Department" id ="MyDeptStep" > <id column ="id" property ="id" /> <id column ="department_name" property ="departmentName" /> <collection property ="emps" select ="com.lun.c03.mapper.dao.EmployeeMapper.getEmpsByDeptId" column ="id" > </collection > </resultMap > <select id ="getDeptByIdStep" resultMap ="MyDeptStep" > select id,department_name from department where id=#{id} </select >
DepartmentMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class DepartmentMapperTest { @Test public void testGetDeptByIdStep () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DepartmentMapper dm = session.getMapper(DepartmentMapper.class); Department department = dm.getDeptByIdStep(1 ); System.out.println(department); System.out.println(department.getEmps()); session.commit(); } finally { session.close(); } } ...
36.映射文件-select-resultMap-分步查询传递多列值&fetchType 扩展:
多列的值传递过去:将多列的值封装map传递;column="{key1=column1,key2=column2}"
fetchType=”lazy”:表示使用延迟加载; 1 2 3 4 5 6 7 8 9 10 11 12 <resultMap type ="com.lun.c01.helloworld.bean.Department" id ="MyDeptStep" > <id column ="id" property ="id" /> <id column ="department_name" property ="departmentName" /> <collection property ="emps" select ="com.lun.c03.mapper.dao.EmployeeMapper.getEmpsByDeptId" column ="{deptId=id}" fetchType ="lazy" > </collection > </resultMap > <select id ="getDeptByIdStep" resultMap ="MyDeptStep" > select id,department_name from department where id=#{id} </select >
37.映射文件-select-resultMap-discriminator鉴别器 EmployeeMapper.java
1 2 3 4 5 6 public interface EmployeeMapper { public List<Employee> getEmpsWithDiscriminator () ; ...
EmployeeMapper.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 39 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <resultMap type ="com.lun.c01.helloworld.bean.Employee" id ="MyEmpDis" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="email" property ="email" /> <result column ="gender" property ="gender" /> <discriminator javaType ="string" column ="gender" > <case value ="0" resultType ="com.lun.c01.helloworld.bean.Employee" > <association property ="department" select ="com.lun.c03.mapper.dao.DepartmentMapper.getDeptById" column ="department_id" fetchType ="eager" > </association > </case > <case value ="1" resultType ="com.lun.c01.helloworld.bean.Employee" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="last_name" property ="email" /> <result column ="gender" property ="gender" /> </case > </discriminator > </resultMap > <select id ="getEmpsWithDiscriminator" resultMap ="MyEmpDis" > select * from employee limit 10 </select >
EmployeeMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testGetEmpsWithDiscriminator () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); System.out.println(em.getEmpsWithDiscriminator()); session.commit(); } finally { session.close(); } }
38.动态sql-简介&环境搭建 动态SQL是MyBatis强大特性之一。极大的简化我们拼装SQL的操作。 动态SQL元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。 MyBatis采用功能强大的基于 OGNL 的表达式来简化操作。if choose (when, otherwise) trim (where, set) foreach 39.动态sql-if-判断&OGNL DynamicSQLMapper.java
1 2 3 4 5 public interface DynamicSQLMapper { public List<Employee> getEmpsByConditionIf (Employee employee) ; }
DynamicSQLMapper.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 <mapper namespace ="com.lun.c04.dynamicsql.DynamicSQLMapper" > <select id ="getEmpsByConditionIf" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where <if test ="id!=null" > id=#{id} </if > <if test ="lastName!=null & & lastName!=" " " > and last_name like #{lastName} </if > <if test ="email!=null and email.trim()!=" " " > and email=#{email} </if > <if test ="gender==0 or gender==1" > and gender=#{gender} </if > </select > </mapper >
DynamicSQLMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class DynamicSQLMapperTest { @Test public void testGetEmpsByConditionIf () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); Employee employee = new Employee (1 , "jallen" , null , null ); System.out.println(dsm.getEmpsByConditionIf(employee)); session.commit(); } finally { session.close(); } } }
OGNL OGNL官方文档
OGNL( Object Graph Navigation Language )对象图导航语言, 这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL, SpEL等。
访问对象属性: person.name 调用方法: person.getName() 调用静态属性/方法: @java.lang.Math@PI、@java.util.UUID@randomUUID() 调用构造方法: new com.lun.Person(‘admin’).name 运算符: +, -*, /, % 逻辑运算符: in, not in, >, >=, <, <=, ==, != 注意 : xml中特殊符号如”,>,<等这些都需要使用转义字符
访问集合伪属性:
类型 伪属性 伪属性对应的 Java 方法 List、 Set、 Map size、 isEmpty List/Set/Map.size(),List/Set/Map.isEmpty() List、 Set iterator List.iterator()、 Set.iterator() Map keys、 values Map.keySet()、 Map.values() Iterator next、 hasNext Iterator.next()、 Iterator.hasNext()
40.动态sql-where-查询条件 查询的时候如果某些条件没带可能sql拼装会有问题
给where后面加上1=1,以后的条件都and xxx。 mybatis使用where标签来将所有的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉(where只会去掉第一个多出来的and或者or,但最后一个多出来的and或者or则不会去掉 )。 DynamicSQLMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <mapper namespace ="com.lun.c04.dynamicsql.DynamicSQLMapper" > <select id ="getEmpsByConditionIfWithWhere" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee <where > <if test ="id!=null" > id=#{id} </if > <if test ="lastName!=null & & lastName!=" " " > and last_name like #{lastName} </if > <if test ="email!=null and email.trim()!=" " " > and email=#{email} </if > <if test ="gender==0 or gender==1" > and gender=#{gender} </if > </where > </select > </select >
DynamicSQLMapper.java
1 2 3 public interface DynamicSQLMapper { public List<Employee> getEmpsByConditionIfWithWhere (Employee employee) ;
DynamicSQLMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class DynamicSQLMapperTest { @Test public void testGetEmpsByConditionIfWithWhere () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); Employee employee = new Employee (null , "jallen" , null , null ); System.out.println(dsm.getEmpsByConditionIfWithWhere(employee)); session.commit(); } finally { session.close(); } } ...
41.动态sql-trim-自定义字符串截取 后面多出的and或者or where标签不能解决
prefix=””:前缀:trim标签体中是整个字符串拼串后的结果。 prefixOverrides=””: suffix=””:后缀 suffixOverrides=”” DynamicSQLMapper.java
1 2 3 public interface DynamicSQLMapper { public List<Employee> getEmpsByConditionTrim (Employee employee) ; ...
DynamicSQLMapper.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 <mapper namespace ="com.lun.c04.dynamicsql.DynamicSQLMapper" > <select id ="getEmpsByConditionTrim" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee <trim prefix ="where" suffixOverrides ="and" > <if test ="id!=null" > id=#{id} and </if > <if test ="lastName!=null & & lastName!=" " " > last_name like #{lastName} and </if > <if test ="email!=null and email.trim()!=" " " > email=#{email} and </if > <if test ="gender==0 or gender==1" > gender=#{gender} </if > </trim > </select > ...
DynamicSQLMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class DynamicSQLMapperTest { @Test public void testGetEmpsByConditionTrim () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); Employee employee = new Employee (1 , "jallen" , null , null ); System.out.println(dsm.getEmpsByConditionTrim(employee)); session.commit(); } finally { session.close(); } } ...
42.动态sql-choose-分支选择 DynamicSQLMapper.java
1 2 3 4 5 6 public interface DynamicSQLMapper { public List<Employee> getEmpsByConditionChoose (Employee employee) ; ...
DynamicSQLMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <mapper namespace ="com.lun.c04.dynamicsql.DynamicSQLMapper" > <select id ="getEmpsByConditionChoose" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee <where > <choose > <when test ="id!=null" > id=#{id} </when > <when test ="lastName!=null" > last_name like #{lastName} </when > <when test ="email!=null" > email = #{email} </when > <otherwise > gender = 0 </otherwise > </choose > </where > </select >
DynamicSQLMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class DynamicSQLMapperTest { @Test public void testGetEmpsByConditionChoose () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); Employee employee = new Employee (1 , "jallen" , null , null ); System.out.println(dsm.getEmpsByConditionChoose(employee)); session.commit(); } finally { session.close(); } }
43.动态sql-set-与if结合的动态更新 DynamicSQLMapper.java
1 2 3 public interface DynamicSQLMapper { public void updateEmp (Employee employee) ;
DynamicSQLMapper.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 <mapper namespace ="com.lun.c04.dynamicsql.DynamicSQLMapper" > <update id ="updateEmp" > update employee <set > <if test ="lastName!=null" > last_name=#{lastName}, </if > <if test ="email!=null" > email=#{email}, </if > <if test ="gender!=null" > gender=#{gender} </if > </set > where id=#{id} </update >
DynamicSQLMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class DynamicSQLMapperTest { @Test public void testUpdateEmp () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); Employee employee = new Employee (1 , "jallen2" , null , null ); dsm.updateEmp(employee); session.commit(); } finally { session.close(); } }
44.动态sql-foreach-遍历集合 DynamicSQLMapper.java
1 2 3 public interface DynamicSQLMapper { public List<Employee> getEmpsByConditionForeach (@Param("ids") List<Integer> ids) ;
DynamicSQLMapper.xml
1 2 3 4 5 6 7 8 9 10 <mapper namespace="com.lun.c04.dynamicsql.DynamicSQLMapper" > <!--public List<Employee> getEmpsByConditionForeach (List<Integer> ids) ; --> <select id="getEmpsByConditionForeach" resultType="com.lun.c01.helloworld.bean.Employee" > select * from employee <foreach collection="ids" item="item_id" separator="," open="where id in(" close=")" > #{item_id} </foreach> </select>
DynamicSQLMapperTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class DynamicSQLMapperTest { @Test public void testGetEmpsByConditionForeach () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); System.out.println(dsm.getEmpsByConditionForeach(Arrays.asList(1 ,2 ,3 ))); session.commit(); } finally { session.close(); } }
45.动态sql-foreach-mysql下foreach批量插入的两种方式 DynamicSQLMapper.java
1 2 3 4 5 6 public interface DynamicSQLMapper { public void addEmps (@Param("emps") List<Employee> emps) ; public void addEmps2 (@Param("emps") List<Employee> emps) ;
DynamicSQLMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <mapper namespace ="com.lun.c04.dynamicsql.DynamicSQLMapper" > <insert id ="addEmps" > insert into employee(last_name,email,gender,department_id) values <foreach collection ="emps" item ="emp" separator ="," > (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id}) </foreach > </insert > <insert id ="addEmps2" > <foreach collection ="emps" item ="emp" separator =";" > insert into employee(last_name,email,gender,department_id) values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id}) </foreach > </insert >
注意 ,MySQL数据库连接属性allowMultiQueries=true,才能批量删除,修改数据。(在连接MySQL的URL后添加参数)。
DynamicSQLMapperTest.java
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 public class DynamicSQLMapperTest { @Test public void testAddEmps2() throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml"); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); dsm.addEmps2(Arrays.asList(new Employee(null, "abc", null, null), new Employee(null, "cba",null, null))); session.commit(); } finally { session.close(); } } @Test public void testAddEmps() throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c04/mybatis-config.xml"); SqlSession session = ssf.openSession(); try { DynamicSQLMapper dsm = session.getMapper(DynamicSQLMapper.class); dsm.addEmps(Arrays.asList(new Employee(null, "abc", null, null), new Employee(null, "cba",null, null))); session.commit(); } finally { session.close(); } }
46.动态sql-foreach-oracle下批量插入的两种方式 Oracle数据库批量保存:
Oracle支持的批量方式:
多个insert放在begin - end里面 利用中间表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 多个insert 放在begin - end 里面 begin insert into employees(employee_id,last_name,email) values (employees_seq.nextval,'test_001' ,'test_001@atguigu.com' ); insert into employees(employee_id,last_name,email) values (employees_seq.nextval,'test_002' ,'test_002@atguigu.com' ); end ;# 利用中间表 insert into employees(employee_id,last_name,email) select employees_seq.nextval,lastName,email from ( select 'test_a_01' lastName,'test_a_e01' email from dual union select 'test_a_02' lastName,'test_a_e02' email from dual union select 'test_a_03' lastName,'test_a_e03' email from dual );
47.动态sql-foreach-oracle下foreach批量保存两种方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <insert id ="addEmps" databaseId ="oracle" > insert into employees(employee_id,last_name,email) <foreach collection ="emps" item ="emp" separator ="union" open ="select employees_seq.nextval,lastName,email from(" close =")" > select #{emp.lastName} lastName,#{emp.email} email from dual </foreach > </insert >
48.动态sql-内置参数 _parameter & _databaseId 不只是方法传递过来的参数可以被用来判断,
mybatis默认还有两个内置参数 :
_parameter :代表整个参数单个参数:_parameter就是这个参数 多个参数:参数会被封装为一个map;_parameter就是代表这个map _databaseId :如果配置了databaseIdProvider标签。_databaseId就是代表当前数据库的别名oracle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="getEmpsTestInnerParameter" resultType ="com.lun.c01.helloworld.bean.Employee" > <if test ="_databaseId=='mysql'" > select * from tbl_employee <if test ="_parameter!=null" > where last_name like #{_parameter.lastName} </if > </if > <if test ="_databaseId=='oracle'" > select * from employees <if test ="_parameter!=null" > where last_name like #{_parameter.lastName} </if > </if > </select >
49.动态sql-bind-绑定(不常用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <select id ="getEmpsTestInnerParameter" resultType ="com.atguigu.mybatis.bean.Employee" > <bind name ="lastName" value ="'%'+lastName+'%'" /> <if test ="_databaseId=='mysql'" > select * from tbl_employee <if test ="_parameter!=null" > where last_name like #{lastName} </if > </if > <if test ="_databaseId=='oracle'" > select * from employees <if test ="_parameter!=null" > where last_name like #{_parameter.lastName} </if > </if > </select >
50.动态sql-sql-抽取可重用的sql片段 抽取可重用的sql片段。方便后面引用:
sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用 include来引用已经抽取的sql: include还可以自定义一些property,sql标签内部就能使用自定义的属性include-property:取值的正确方式${prop}, 不能使用#{}
,而使用${}
1 2 3 4 5 6 7 8 9 10 < sql id= "userColumns"> ${alias}.id,${alias}.username,${alias}.password < / sql > < select id= "selectUsers" resultType= "map"> select < include refid= "userColumns"> < property name= "alias" value = "t1"/ > < / include> , < include refid= "userColumns"> < property name= "alias" value = "t2"/ > < / include> from some_table t1 cross join some_table t2 < / select >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 < sql id= "insertColumn"> < if test= "_databaseId=='oracle'"> employee_id,last_name,email < / if> < if test= "_databaseId=='mysql'"> last_name,email,gender,d_id < / if> < / sql > ... < insert id= "addEmps"> insert into tbl_employee( < include refid= "insertColumn"> < / include> < ! ) values < foreach collection= "emps" item= "emp" separator= ","> (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}) < / foreach> < / insert >
51.缓存-缓存介绍 MyBatis官方文档
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存,一级缓存和二级缓存。
默认情况下,只有一级缓存( SqlSession级别的缓存,也称为本地缓存)开启。 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。 为了提高扩展性。 MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存 52.缓存-一级缓存体验 一级缓存 一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域. 在mybatis3.1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置 - - - - localCacheScope MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be used just for statement execution, no data will be shared between two different calls to the same SqlSession. SESSION/STATEMENT SESSION
一级缓存体验 CacheTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class CacheTest { @Test public void testFirstCache () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c03/mybatis-config.xml" ); SqlSession session = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); Employee e1 = em.getEmpById(1 ); System.out.println(e1); Employee e2 = em.getEmpById(1 ); System.out.println(e2); System.out.println("e1 == e2 : " + (e1 == e2)); session.commit(); } finally { session.close(); } } }
输出结果:
1 2 3 4 5 6 DEBUG 08-02 22:50:35,092 ==> Preparing: select * from employee where id = ? (BaseJdbcLogger.java:145) DEBUG 08-02 22:50:35,192 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) DEBUG 08-02 22:50:35,260 <== Total: 1 (BaseJdbcLogger.java:145) Employee [id=1, lastName=jallen2, email=jallen@good.com, gender=1, department=null] Employee [id=1, lastName=jallen2, email=jallen@good.com, gender=1, department=null] e1 == e2 : true <-------------e1和e2指向相同的对象
53.缓存-一级缓存失效的四种情况 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
key = hashCode + 查询的SqlId + 编写的sql查询语句 + 参数 一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存 同一个SqlSession但是查询条件不同 同一个SqlSession两次查询期间执行了任何一次增删改 操作 同一个SqlSession两次查询期间手动清空了缓存 54.缓存-二级缓存介绍 二级缓存(second level cache),全局作用域缓存 二级缓存默认不开启,需要手动配置 MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口 二级缓存在 SqlSession 关闭或提交之后才会生效 使用步骤全局配置文件中开启二级缓存<setting name="cacheEnabled" value="true"/>
需要使用二级缓存的映射文件处使用cache配置缓存 注意 : POJO需要实现Serializable接口 cache标签的属性:
eviction:缓存的回收策略:LRU – 最近最少使用的:移除最长时间不被使用的对象。 FIFO – 先进先出:按对象进入缓存的顺序来移除它们。 SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 默认的是 LRU。 flushInterval:缓存刷新间隔 readOnly:是否只读:true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快 false:非只读:mybatis觉得获取的数据可能会被修改。mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢 size:缓存存放多少元素; type=””:指定自定义缓存的全类名; 55.缓存-二级缓存使用&细节 使用步骤:
全局配置文件中开启二级缓存<setting name="cacheEnabled" value="true"/>
需要使用二级缓存的映射文件处使用cache配置缓存 注意 : POJO需要实现Serializable接口mybatis-config.xml
1 2 3 <configuration> <settings> <setting name="cacheEnabled" value="true"/>
Employee.java
1 2 3 4 public class Employee implements Serializable { private static final long serialVersionUID = -7390587151857533202L ;
EmployeeMapper.xml
1 2 3 <mapper namespace ="com.lun.c03.mapper.dao.EmployeeMapper" > <cache eviction ="FIFO" flushInterval ="60000" readOnly ="false" size ="1024" > </cache >
CacheTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class CacheTest { @Test public void testSecondCache () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c05/mybatis-config.xml" ); SqlSession session = ssf.openSession(); SqlSession session2 = ssf.openSession(); try { EmployeeMapper em = session.getMapper(EmployeeMapper.class); Employee e1 = em.getEmpById(1 ); System.out.println(e1); session.close(); EmployeeMapper em2 = session2.getMapper(EmployeeMapper.class); Employee e2 = em2.getEmpById(1 ); System.out.println(e2); System.out.println("e1 == e2 : " + (e1 == e2)); } finally { session2.close(); } }
输出结果:
1 2 3 4 5 6 7 8 DEBUG 08-03 01:13:02,575 Cache Hit Ratio [com.lun.c03.mapper.dao.EmployeeMapper]: 0.0 (LoggingCache.java:62) DEBUG 08-03 01:13:03,945 ==> Preparing: select * from employee where id = ? (BaseJdbcLogger.java:145) DEBUG 08-03 01:13:04,081 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) DEBUG 08-03 01:13:04,186 <== Total: 1 (BaseJdbcLogger.java:145) Employee [id=1, lastName=jallen2, email=jallen@good.com, gender=1, department=null] DEBUG 08-03 01:13:04,218 Cache Hit Ratio [com.lun.c03.mapper.dao.EmployeeMapper]: 0.5 (LoggingCache.java:62) Employee [id=1, lastName=jallen2, email=jallen@good.com, gender=1, department=null] e1 == e2 : false
56.缓存-缓存有关的设置以及属性 全局setting的cacheEnable: – 配置二级缓存的开关。一级缓存一直是打开的。 select标签的useCache属性: – 配置这个select是否使用二级缓存。一级缓存一直是使用的 每个增删改标签的flushCache属性: – 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。 sqlSession.clearCache(): – 只是用来清除一级缓存。 全局setting的localCacheScope:本地缓存作用域:(一级缓存SESSION),当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存。 57.缓存-缓存原理图示
58.缓存-第三方缓存整合原理&ehcache适配包下载 EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。 MyBatis定义了Cache接口方便我们进行自定义扩展。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package org.apache.ibatis.cache;import java.util.concurrent.locks.ReadWriteLock;public interface Cache { String getId () ; void putObject (Object key, Object value) ; Object getObject (Object key) ; Object removeObject (Object key) ; void clear () ; int getSize () ; ReadWriteLock getReadWriteLock () ; }
59.缓存-MyBatis整合ehcache&总结 步骤:
1 2 3 4 5 6 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency >
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 <?xml version="1.0" encoding="UTF-8" ?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="../config/ehcache.xsd" > <diskStore path ="C:\\ehcache" /> <defaultCache maxElementsInMemory ="10000" maxElementsOnDisk ="10000000" eternal ="false" overflowToDisk ="true" timeToIdleSeconds ="120" timeToLiveSeconds ="120" diskExpiryThreadIntervalSeconds ="120" memoryStoreEvictionPolicy ="LRU" > </defaultCache > </ehcache >
配置cache标签<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
演示:
DepartmentMapper.xml
1 2 3 4 <mapper namespace ="com.lun.c03.mapper.dao.DepartmentMapper" > <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
CacheTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class CacheTest { @Test public void testEhcache () throws IOException { SqlSessionFactory ssf = Tools.getSqlSessionFactory("c05/mybatis-config.xml" ); SqlSession session = ssf.openSession(); SqlSession session2 = ssf.openSession(); try { DepartmentMapper dm = session.getMapper(DepartmentMapper.class); Department dp = dm.getDeptById(1 ); System.out.println(dp); session.close(); DepartmentMapper dm2 = session2.getMapper(DepartmentMapper.class); Department dp2 = dm2.getDeptById(1 ); System.out.println(dp2); } finally { session2.close(); } }
另外 :
参照缓存: 若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。
1 2 3 <mapper namespace ="com.lun.mybatis.dao.DepartmentMapper" > <cache-ref namespace ="com.lun.mybatis.dao.EmployeeMapper" />
60.整合Spring-整合适配包下载 MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
官方文档
源码仓库
官方整合示例
61.整合Spring-所有需要的jar包导入 pom.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 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 72 73 74 75 76 77 <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.5.5</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 3.1.0</version > <scope > provided</scope > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.1</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-orm</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aop</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aspects</artifactId > <version > ${springframework.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > ${springframework.version}</version > <scope > test</scope > </dependency >
62.整合Spring-引入MyBatis之前的配置 mybatis-config.xml
1 2 3 4 5 6 7 8 9 <configuration > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> <setting name ="cacheEnabled" value ="true" /> </settings > </configuration >
EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 <mapper namespace ="com.lun.c06.spring.EmployeeMapper" > <select id ="getEmpById" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee where id = #{id} </select > <select id ="getEmps" resultType ="com.lun.c01.helloworld.bean.Employee" > select * from employee </select > </mapper >
EmployeeMapper
1 2 3 4 5 6 7 public interface EmployeeMapper { public Employee getEmpById (Integer id) ; public List<Employee> getEmps () ; }
63.整合Spring-SpringMVC配置文件编写 web.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 <?xml version="1.0" encoding="UTF-8" ?> <web-app ... > <context-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:applicationContext.xml</param-value > </context-param > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener</listener-class > </listener > <servlet > <servlet-name > spring</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > spring</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
spring-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <beans ... "> <context:component-scan base-package ="com.lun.c06" use-default-filters ="false" > <context:include-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan > <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/pages/" > </property > <property name ="suffix" value =".jsp" > </property > </bean > <mvc:annotation-driven > </mvc:annotation-driven > <mvc:default-servlet-handler /> </beans >
64.整合Spring-Spring配置文件编写 applicationContext.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 <beans ... > <context:component-scan base-package ="com.lun.c06" > <context:exclude-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan > <context:property-placeholder location ="classpath:c04/dbconfig.properties" /> <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="jdbcUrl" value ="${jdbc.url}" > </property > <property name ="driverClass" value ="${jdbc.driver}" > </property > <property name ="user" value ="${jdbc.username}" > </property > <property name ="password" value ="${jdbc.password}" > </property > </bean > <bean id ="dataSourceTransactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <tx:annotation-driven transaction-manager ="dataSourceTransactionManager" /> ...
65.整合Spring-Spring整合MyBatis关键配置 applicationContext.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 ... <bean id ="sqlSessionFactoryBean" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" > </property > <property name ="configLocation" value ="classpath:c06/mybatis-config.xml" > </property > <property name ="mapperLocations" value ="classpath:c06/*Mapper.xml" > </property > </bean > <bean id ="sqlSession" class ="org.mybatis.spring.SqlSessionTemplate" > <constructor-arg name ="sqlSessionFactory" ref ="sqlSessionFactoryBean" > </constructor-arg > <constructor-arg name ="executorType" value ="BATCH" > </constructor-arg > </bean > <mybatis-spring:scan base-package ="com.lun.c06" /> </beans >
66.整合Spring-整合测试 EmployeeController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller public class EmployeeController implements Serializable { private static final long serialVersionUID = -5415910268862124882L ; @Autowired EmployeeService employeeService; @RequestMapping("/getemps") public String emps (Map<String,Object> map) { List<Employee> emps = employeeService.getEmps(); map.put("allEmps" , emps); return "list" ; } }
EmployeeService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Service public class EmployeeService { @Autowired private EmployeeMapper employeeMapper; @Autowired private SqlSession sqlSession; public List<Employee> getEmps () { return employeeMapper.getEmps(); } }
list.jsp
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 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html > <head > <meta http-equiv ="Content-Type" content ="text/html; charset=UTF-8" > <title > 员工列表</title > </head > <body > <table > <tr > <td > id</td > <td > lastName</td > <td > email</td > <td > gender</td > </tr > <c:forEach items ="${allEmps}" var ="emp" > <tr > <td > ${emp.id}</td > <td > ${emp.lastName}</td > <td > ${emp.email}</td > <td > ${emp.gender}</td > </tr > </c:forEach > </table > </body > </html >
最后,在浏览器输入http://localhost:8080/LearnMybatis/getemps
进行测试
MyBatis-代码生成器 MyBatis Generator:
简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写
官方文档地址
官方工程地址
添加依赖 1 2 3 4 5 <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.3.7</version > </dependency >
使用说明 使用步骤:编写MBG的配置文件(重要几处配置)jdbcConnection配置数据库连接信息 javaModelGenerator配置javaBean的生成策略 sqlMapGenerator 配置sql映射文件生成策略 javaClientGenerator配置Mapper接口的生成策略 table 配置要逆向解析的数据表tableName:表名 domainObjectName:对应的javaBean名 运行代码生成器生成代码 注意:Context标签targetRuntime=“MyBatis3“可以生成带条件的增删改查 targetRuntime=“MyBatis3Simple“可以生成基本的增删改查 如果再次生成,建议将之前生成的数据删除,避免xml向后追加内容出现的问题。 配置文件 mbg.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 39 40 41 42 43 44 45 46 47 48 49 <generatorConfiguration > <properties resource ="c04/dbconfig.properties" /> <context id ="DB2Tables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="${jdbc.driver}" connectionURL ="${jdbc.url}" userId ="${jdbc.username}" password ="${jdbc.password}" > </jdbcConnection > <javaTypeResolver > <property name ="forceBigDecimals" value ="false" /> </javaTypeResolver > <javaModelGenerator targetPackage ="com.lun.bean" targetProject ="C:\\src" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.lun.dao" targetProject ="C:\\src" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.lun.dao" targetProject ="C:\\src" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="department" domainObjectName ="Department" > </table > <table tableName ="employee" domainObjectName ="Employee" > </table > </context > </generatorConfiguration >
生成器代码 GenerateCode.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class GenerateCode { public static void main (String[] args) { try { List<String> warnings = new ArrayList <String>(); boolean overwrite = true ; File configFile = new File (GenerateCode.class.getClassLoader().getResource("c07/mbg.xml" ).getFile()); ConfigurationParser cp = new ConfigurationParser (warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback (overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator (config, callback, warnings); myBatisGenerator.generate(null ); } catch (Exception e) { e.printStackTrace(); } } }
QBC风格的带条件查询 生成器会QBC风格代码
QBC(Query By Criteria) API提供了检索对象的另一种方式,它主要由Criteria接口、Criterion接口和Expresson类组成,它支持在运行时动态生成查询语句。
From https://baike.baidu.com/item/QBC/1529451
QBC风格代码使用实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void test01 () { SqlSession openSession = build.openSession(); DeptMapper mapper = openSession.getMapper(DeptMapper.class); DeptExample example = new DeptExample (); Criteria criteria = example.createCriteria(); criteria.andDeptnameLike("%部%" ); criteria.andIdGreaterThan(2 ); List<Dept> list = mapper.selectByExample(example); for (Dept dept : list) { System.out.println(dept); } }
MyBatis-工作原理
Configuration封装了所有配置文件的详细信息 总结:把配置文件的信息解析并保存在Configuration对象中,返回包含了Configuration的DefaultSqlSession对象。
返回SqlSession的实现类DefaultSqlSession对象。他里面包含了Executor和Configuration Executor会在这一步被创建
StatementHandler:处理sql语句预编译,设置参数等相关工作; ParameterHandler:设置预编译参数用的 ResultHandler:处理结果集 TypeHandler:在整个过程中,进行数据库类型和javaBean类型的映射 小结 根据配置文件(全局,sql映射)初始化出Configuration对象 创建一个DefaultSqlSession对象,它里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor) DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy; MapperProxy里面有(DefaultSqlSession); 执行增删改查方法:调用DefaultSqlSession的增删改查(Executor); 会创建一个StatementHandler对象。同时也会创建出ParameterHandler和ResultSetHandler) 调用StatementHandler预编译参数以及设置参数值,使用ParameterHandler来给sql设置参数 调用StatementHandler的增删改查方法; ResultSetHandler封装结果 注意 :四大对象(Executor、ParameterHandler、ResultSetHandler)每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);
MyBatis-插件开发 在四大对象创建的时候
每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler); 获取到所有的Interceptor(拦截器)(插件需要实现的接口);调用interceptor.plugin(target);返回target包装后的对象 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)我们的插件可以为四大对象创建出代理对象;代理对象就可以拦截到四大对象的每一个执行; 1 2 3 4 5 6 public Object pluginAll (Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) ParameterHandler (getParameterObject, setParameters) ResultSetHandler (handleResultSets, handleOutputParameters) StatementHandler (prepare, parameterize, batch, update, query) 插件开发 插件开发步骤:
编写插件实现Interceptor接口,并使用 @Intercepts注解完成插件签名 在全局配置文件中注册插件 MyFirstPlugin.java
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 @Intercepts({ @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class) }) public class MyFirstPlugin implements Interceptor { @Override public Object intercept (Invocation invocation) throws Throwable { System.out.println("myfirstplugin...intercept:" + invocation.getMethod()); Object target = invocation.getTarget(); System.out.println("当前拦截到的对象:" +target); MetaObject metaObject = SystemMetaObject.forObject(target); Object value = metaObject.getValue("parameterHandler.parameterObject" ); System.out.println("sql语句中的参数是:" +value); metaObject.setValue("parameterHandler.parameterObject" , 3 ); Object proceed = invocation.proceed(); return proceed; } @Override public Object plugin (Object target) { System.out.println("-->myfirstplugin...plugin,将要包装的对象:" +target); Object wrap = Plugin.wrap(target, this ); return wrap; } @Override public void setProperties (Properties properties) { System.out.println("插件配置的信息:" +properties); } }
mybatis-config.xml
1 2 3 4 5 6 7 8 <configuration > <plugins > <plugin interceptor ="com.lun.c08.interceptor.MyFirstPlugin" > </plugin > <plugin interceptor ="com.lun.c08.interceptor.MySecondPlugin" > </plugin > </plugins > ...
插件原理 按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理 多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链 目标方法执行时依次从外到内执行插件的intercept方法。 多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值
Interceptor接口 Intercept:拦截目标方法执行 plugin:生成动态代理对象,可以使用MyBatis提供的Plugin类的wrap方法 setProperties:注入插件配置时设置的属性 常用代码 从代理链中分离真实被代理对象
1 2 3 4 5 6 7 8 9 10 11 while 循环分离出最终被代理对象,从而方便提取信息MetaObject metaObject = SystemMetaObject.forObject(target);while (metaObject.hasGetter("h" )) { Object h = metaObject.getValue("h" ); metaObject = SystemMetaObject.forObject(h); } Object obj = metaObject.getValue("target" );MetaObject forObject = SystemMetaObject.forObject(obj);
扩展-MyBatis实用场景-PageHelper插件进行分页 PageHelper插件官网
pom.xml 添加依赖
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 5 6 7 <configuration > <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > </plugin > </plugins > ...
使用示例
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 public class PageHelperTest { @Test public void test () throws IOException { SqlSessionFactory sqlSessionFactory = Tools.getSqlSessionFactory("c09/mybatis-config.xml" ); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Page<Object> page = PageHelper.startPage(5 , 1 ); List<Employee> emps = mapper.getEmps(); PageInfo<Employee> info = new PageInfo <>(emps, 5 ); for (Employee employee : emps) { System.out.println(employee); } System.out.println("当前页码:" +info.getPageNum()); System.out.println("总记录数:" +info.getTotal()); System.out.println("每页的记录数:" +info.getPageSize()); System.out.println("总页码:" +info.getPages()); System.out.println("是否第一页:" +info.isIsFirstPage()); System.out.println("连续显示的页码:" ); int [] nums = info.getNavigatepageNums(); for (int i = 0 ; i < nums.length; i++) { System.out.println(nums[i]); } } finally { openSession.close(); } } }
扩展-MyBatis实用场景-批量操作 默认的 openSession() 方法没有参数,它会创建有如下特性的会开启一个事务(也就是不自动提交) 连接对象会从由活动环境配置的数据源实例得到。 事务隔离级别将会使用驱动或数据源的默认设置。 预处理语句不会被复用,也不会批量处理更新。 openSession 方法的 ExecutorType 类型的参数,枚举类型:ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配的)。它为每个语句的执行创建一个新的预处理语句。 ExecutorType.REUSE: 这个执行器类型会复用预处理语句。 ExecutorType.BATCH: 这个执行器会批量执行所有更新语句
批量操作我们是使用MyBatis提供的BatchExecutor进行的,它的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发给数据库一次。
BatchTest.java
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 public class BatchTest { @Test public void test () throws IOException { SqlSessionFactory sqlSessionFactory = Tools.getSqlSessionFactory("c09/mybatis-config.xml" ); SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH); long start = System.currentTimeMillis(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); for (int i = 0 ; i < 10000 ; i++) { mapper.addEmp(new Employee (UUID.randomUUID().toString().substring(0 , 5 ), "b" , "1" )); } openSession.commit(); long end = System.currentTimeMillis(); System.out.println("执行时长:" +(end-start)); }finally { openSession.close(); } } }
与Spring整合中,我们推荐,额外的配置一个可以专门用来执行批量操作的SqlSession
1 2 3 4 5 <bean id ="sqlSession" class ="org.mybatis.spring.SqlSessionTemplate" > <constructor-arg name ="sqlSessionFactory" ref ="sqlSessionFactoryBean" > </constructor-arg > <constructor-arg name ="executorType" value ="BATCH" > </constructor-arg > </bean >
需要用到批量操作的时候,我们可以注入配置的这个批量SqlSession。通过他获取到mapper映射器进行操作。
1 2 3 4 5 6 7 @Service public class EmployeeService { @Autowired private SqlSession sqlSession; ...
注意:
批量操作是在session.commit()以后才发送sql语句给数据库进行执行的 如果我们想让其提前执行,以方便后续可能的查询操作获取数据,我们可以使用sqlSession.flushStatements()方法,让其直接冲刷到数据库进行执行。 扩展-MyBatis实用场景-存储过程 实际开发中,我们通常也会写一些存储过程,MyBatis也支持对存储过程的调用
一个最简单的存储过程:
1 2 3 4 5 6 delimiter $$ create procedure test() begin select 'hello'; end $$ delimiter ;
存储过程的调用:
select标签中statementType=“CALLABLE” 标签体中调用语法:{call procedure_name(#{param1_info},#{param2_info})}
MyBatis对存储过程的游标提供了一个JdbcType=CURSOR的支持,可以智能的把游标读取到的数据,映射到我们声明的结果集中
oracle存储过程
1 2 3 4 5 6 7 8 9 10 create or replace procedure hello_ test( p_start in int ,p_end in int ,p_count out int ,p_emps out sys_refcursor) ) as begin select count (* ) into p_count from employees; open p_emps for select * from (select rownum rn,e.* from employees e where rownum<= p_end ) where rn>= p_start; end hello_test;
mapper.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <mapper namespace ="com.lun.dao.EmployeeMapper" > <select id ="getPageByProcedure" statementType ="CALLABLE" databaseId ="oracle" > {call hello_test( #{start,mode=IN,jdbcType=INTEGER}, #{end,mode=IN,jdbcType=INTEGER}, #{count,mode=OUT,jdbcType=INTEGER}, #{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=PageEmp} )} </select > <resultMap type ="com.lun.bean.Employee" id ="PageEmp" > <id column ="EMPLOYEE_ID" property ="id" /> <result column ="LAST_NAME" property ="email" /> <result column ="EMAIL" property ="email" /> </resultMap >
全局配置文件
1 2 3 4 <databaseIdProvider type ="DB_VENDOR" > <property name ="MySQL" value ="mysql" /> <property name ="Oracle" value ="oracle" /> </databaseIdProvider >
测试
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 @Test public void testProcedure () throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); OraclePage page = new OraclePage (); page.setStart(1 ); page.setEnd(5 ); mapper.getPageByProcedure(page); System.out.println("总记录数:" +page.getCount()); System.out.println("查出的数据:" +page.getEmps().size()); System.out.println("查出的数据:" +page.getEmps()); }finally { openSession.close(); } }
1 2 3 4 5 6 7 8 public class OraclePage { private int start; private int end; private int count; private List<Employee> emps;
扩展-MyBatis实用场景-typeHandler处理枚举 热身 新建一个枚举类
1 2 3 public enum EmpStatus { LOGIN,LOGOUT,REMOVE }
枚举的相关属性
1 2 3 4 5 6 @Test public void testEnumUse () { EmpStatus login = EmpStatus.LOGIN; System.out.println("枚举的索引:" + login.ordinal()); System.out.println("枚举的名字:" + login.name()); }
输出结果:
若存入枚举属性到数据库,数据库表会呈现怎样的值?
Employee类 添加一个枚举属性
1 2 3 4 5 6 7 8 9 public class Employee implements Serializable { private Integer id; private String lastName; private String email; private String gender; private EmpStatus empStatus = EmpStatus.LOGOUT; ...
修改表结构
1 ALTER TABLE employee ADD COLUMN EmpStatus VARCHAR (255 );
EmployeeMapper.java
1 2 3 4 5 6 7 public interface EmployeeMapper { public void addEmp2 (com.lun.c09.other.bean.Employee em) ; public com.lun.c09.other.bean.Employee getEmpById2 (Integer id) ; }
EmployeeMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 ... <insert id ="addEmp2" parameterType ="com.lun.c09.other.bean.Employee" useGeneratedKeys ="true" keyProperty ="id" > insert into employee(last_name,email,gender,empStatus) values(#{lastName},#{email},#{gender},#{empStatus}) </insert > <select id ="getEmpById2" resultType ="com.lun.c09.other.bean.Employee" > select id,last_name lastName,email,gender,empStatus from employee where id = #{id} </select > </mapper >
测试方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void testEnum () throws IOException{ SqlSessionFactory sqlSessionFactory = Tools.getSqlSessionFactory("c09/mybatis-config.xml" ); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee employee = new Employee ("test_enum" , "enum@abc.com" ,"1" ); mapper.addEmp2(employee); Employee empById = mapper.getEmpById2(employee.getId()); System.out.println(empById.getEmpStatus()); }finally { openSession.close(); } }
结果是存入LOGOUT
到数据库表。
因此,默认mybatis在处理枚举对象的时候保存的是枚举的名字:EnumTypeHandler
将想存数值 到数据库表,改用EnumOrdinalTypeHandler
在MyBatis存储文件 注册
1 2 3 4 5 6 7 8 <configuration > <typeHandlers > <typeHandler handler ="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType ="com.lun.c09.other.bean.EmpStatus" /> </typeHandlers > ...
自定义TypeHandler 我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略。
步骤:
实现TypeHandler接口或者继承BaseTypeHandler 使用@MappedTypes定义处理的java类型,使用@MappedJdbcTypes定义jdbcType类型 在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理或者在全局配置TypeHandler要处理的javaType。 示例:希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名字
EmpStatus.java
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 public enum EmpStatus { LOGIN(100 ,"用户登录" ),LOGOUT(200 ,"用户登出" ),REMOVE(300 ,"用户不存在" ); private Integer code; private String msg; private EmpStatus (Integer code,String msg) { this .code = code; this .msg = msg; } public static EmpStatus getEmpStatusByCode (Integer code) { switch (code) { case 100 : return LOGIN; case 200 : return LOGOUT; case 300 : return REMOVE; default : return LOGOUT; } } ...
自定义Handler
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 public class MyEnumEmpStatusTypeHandler implements TypeHandler <EmpStatus> { @Override public void setParameter (PreparedStatement ps, int i, EmpStatus parameter, JdbcType jdbcType) throws SQLException { System.out.println("要保存的状态码:" +parameter.getCode()); ps.setString(i, parameter.getCode().toString()); } @Override public EmpStatus getResult (ResultSet rs, String columnName) throws SQLException { int code = rs.getInt(columnName); System.out.println("从数据库中获取的状态码:" +code); EmpStatus status = EmpStatus.getEmpStatusByCode(code); return status; } @Override public EmpStatus getResult (ResultSet rs, int columnIndex) throws SQLException { int code = rs.getInt(columnIndex); System.out.println("从数据库中获取的状态码:" +code); EmpStatus status = EmpStatus.getEmpStatusByCode(code); return status; } @Override public EmpStatus getResult (CallableStatement cs, int columnIndex) throws SQLException { int code = cs.getInt(columnIndex); System.out.println("从数据库中获取的状态码:" +code); EmpStatus status = EmpStatus.getEmpStatusByCode(code); return status; } }
注册自定义TypeHandler:
1.MyBatis全局配置文件 中注册
1 2 3 4 5 6 7 8 <configuration > <typeHandlers > <typeHandler handler ="com.lun.c09.other.typehandler.MyEnumEmpStatusTypeHandler" javaType ="com.lun.c09.other.bean.EmpStatus" /> </typeHandlers > ...
也可以在处理某个字段的时候告诉MyBatis用什么类型处理器 保存:#{empStatus,typeHandler=xxxx}
查询: 1 2 3 4 <resultMap type ="com.lun.bean.Employee" id ="MyEmp" > <id column ="id" property ="id" /> <result column ="empStatus" property ="empStatus" typeHandler ="xxxxx" /> </resultMap >
注意 :如果在参数位置修改TypeHandler,应该保证保存数据和查询数据用的TypeHandler是一样的。