- MyBatis简介
MyBatis是一个和数据库进行交互的持久化层框架(SQL映射框架);
原始的JDBC
- Hibernate-数据库交互的框架(ORM框架) ,相当于一个黑盒子,无需知道里面的细节,但需要多学习HQL语言,且Hibernate是一个全映射框架,部分字段映射很难
MyBatis框架
- MyBatis将重要的步骤抽取出来可以人工定制,其他步骤自动化;
- 重要步骤都是写在配置文件中(好维护);
- 完全解决数据库的优化问题;
- MyBatis底层就是对原生JDBC的一个简单封装;
- 既将java编码与sql抽取了出来,还不会失去自动化功能;半自动的持久化层框架;
- mybatis是一个轻量级的框架;
- HelloWorld
环境搭建
创建Java工程
创建测试库、测试表、工程中的javabean和操作数据库的dao接口
用MyBatis操作数据库
导包
//mysql-connector-java-5.1.37-bin.jar //mybatis-3.4.1.jar //log4j-1.2.17.jar //建议导入日志包;这样的化在mybatis关键的环节就会有日志打印; //log4j(日志框架);依赖类路径下一个log4j.xml配置文件;
写配置
第一个配置文件:(称为mybatis的全局配置文件,比如链接到哪个数据库)
<?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.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_0325"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
第二个配置文件:(编写每一个方法都如何向数据库发送sql语句,如何执行,相当于接口的实现类)
<?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"> <!--namespce:名称空间,写接口的全类名,相当于告诉Mybatis这个配置文件是来实现哪个接口的 --> <mapper namespace="com.atguigu.dao.EmployeeDao"> <!-- public Employee getEmpById(Integer id); --> <!-- select:用来定义一个查询操作 id:方法名,相当于这个配置是对于某个方法的实现 resultType:指定方法运行后的返回值类型;(查询操作必须指定的) #{属性名}:代表取出传递过来的某个参数的值 --> <select id="getEmpById" resultType="com.atguigu.bean.Employee"> select * from t_employee where id=#{id} </select> </mapper>
我们写的dao接口的实现文件,mybatis默认是不知道的,需要在全局配置文件中注册;
<mappers> <!--resource:表示从类路径下找资源 --> <mapper resource="EmployeeDao.xml"/> </mappers>
进行测试
@Test public void test() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //SqlSessionFactory:会话工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee对象 Employee employee=employeeDao.getEmpById(1); System.out.println(employee); } finally{ openSession.close(); } }
使得写MyBatis有提示
在MyBatis的jar包中解压出这两个文件
- 点击windows->preferences->xml->Catalog
- 接下来编写MyBatis的文件就会有提示信息
- 增删改查
全局配置文件
<?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.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_0325"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource> </environment> </environments> <mappers> <!--resource:表示从类路径下找资源 --> <mapper resource="EmployeeDao.xml"/> </mappers> </configuration>
映射文件
<?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"> <!--namespce:名称空间,写接口的全类名,相当于告诉Mybatis这个配置文件是来实现哪个接口的 --> <mapper namespace="com.atguigu.dao.EmployeeDao"> <!-- //按照员工id查询员工 public Employee getEmpById(Integer id); //更新员工 public int updateEmployee(Employee employee); //删除员工 public int deleteEmployee(Integer id); //插入员工 public int insertEmployee(Employee employee); --> <!--public Employee getEmpById(Integer id); 参数类型不用写 --> <select id="getEmpById" resultType="com.atguigu.bean.Employee"> <!--sql语句不要写分号 --> select * from t_employee where id=#{id} </select> <!-- public int updateEmployee(Employee employee); 增删改不用写返回值类型,增删改是返回值影响多少行 MyBatis自动判断,如果是数字{int,long} 如果是boolean(影响0行自动封装false,否则true) #{属性名},从传入的参数对象中取出对应属性的值 --> <update id="updateEmployee"> UPDATE t_employee SET empname=#{empName},gender=#{gender},email=#{email} WHERE id=#{id} </update> <!--public int deleteEmployee(Integer id); --> <delete id="deleteEmployee"> DELETE FROM t_employee WHERE id=#{id} </delete> <!--public int insertEmployee(Employee employee) --> <insert id="insertEmployee"> INSERT INTO t_employee(empname,gender,email) VALUES(#{empName},#{gender},#{email}) </insert> </mapper>
进行测试
package com.atguigu.test; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import com.atguigu.bean.Employee; import com.atguigu.dao.EmployeeDao; public class MyBatisTest { SqlSessionFactory sqlSessionFactory=null; public void init() throws IOException{ //1.根据全局配置文件创建出一个SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //SqlSessionFactory:会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); System.out.println("进行初始化操作"); } @Test public void testQuery() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); //openSession设置为true则会自动提交 SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee对象 Employee employee=employeeDao.getEmpById(1); System.out.println(employee); } finally{ openSession.close(); } } @Test public void testInsert() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee对象 int i=employeeDao.insertEmployee(new Employee(null,"tomcat","2200@qq.com",0)); System.out.println(i); } finally{ openSession.close(); } } @Test public void testDelete() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee对象 boolean i=employeeDao.deleteEmployee(3); System.out.println(i); } finally{ openSession.close(); } } @Test public void testUpdate() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee对象 int i=employeeDao.updateEmployee(new Employee(1,"tomcat","2200@qq.com",0)); System.out.println(i); } finally{ openSession.close(); } } }
两个文件:
- 全局配置文件:mybatis-config.xml;指导mybatis正确运行的一些全局设置;
- SQL映射文件:EmployeeDao.xml;相当于是对Dao接口的一个实现描述
细节
- 获取到的是接口的代理对象;mybatis自动创建的;
- SqlSessionFactory创建SqlSession对象,Factory只new一次就行, SqlSession相当于connection和数据库进行交互的,和数据库的一次会话,就应该创建一个新的sqlSession;
- 全局配置文件的典型属性
properties:properties和spring的context:property-placeholder:一样引用外部配置文件
<!--1.properties和spring的context:property-placeholder:一样引用外部配置文件 --> <!-- resource:从类路径下开始引用 url:引用磁盘路径或者网络路径的资源 --> <properties resource="dbconfig.properties"></properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--配置连接池 --> <dataSource type="POOLED"> <property name="driver" value="${driverclass}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
settings修改mybatis运行时的行为
<!--2.这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。 --> <settings> <!--name,配置项的key,value,配置项的值 --> <!--开启驼峰命名 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
其他设置
设置名 描述 有效值 默认值 cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType
属性来覆盖该项的开关状态。true | false false aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods
)。true | false false (在 3.4.1 及之前的版本中默认为 true) multipleResultSetsEnabled 是否允许单个语句返回多结果集(需要数据库驱动支持)。 true | false true useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 true | false true useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为。 NONE
: 不做任何反应WARNING
: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'
的日志等级必须设置为WARN
)FAILING
: 映射失败 (抛出SqlSessionException
)NONE, WARNING, FAILING NONE defaultExecutorType 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 SIMPLE REUSE BATCH SIMPLE defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 任意正整数 未设置 (null) defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 任意正整数 未设置 (null) defaultResultSetType 指定语句默认的滚动策略。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) 未设置 (null) safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false False safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 true | false True mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString defaultScriptingLanguage 指定动态 SQL 生成使用的默认脚本语言。 一个类型别名或全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver defaultEnumTypeHandler 指定 Enum 使用的默认 TypeHandler
。(新增于 3.4.5)一个类型别名或全限定类名。 org.apache.ibatis.type.EnumTypeHandler callSettersOnNulls 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false false returnInstanceForEmptyRow 当返回行的所有列都是空时,MyBatis默认返回 null
。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | false false logPrefix 指定 MyBatis 增加到日志名称的前缀。 任何字符串 未设置 logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置 proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 以上) vfsImpl 指定 VFS 的实现 自定义 VFS 的实现的类全限定名,以逗号分隔。 未设置 useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters
选项。(新增于 3.4.1)true | false true configurationFactory 指定一个提供 Configuration
实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration()
的方法。(新增于 3.2.3)一个类型别名或完全限定类名。 未设置 shrinkWhitespacesInSql Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5) true | false false 完整配置
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
typeAliases为类起别名
<!--3.类型别名typeAliases --> <typeAliases> <!--typeAlias就是为一个javaBean起别名,别名默认就是类名(不区分大小写),配置文件中就写类名 --> <typeAlias type="com.atguigu.bean.Employee" alias="Employee"/> <!--批量起别名 name=""指定包名,默认别名就是类名 --> <package name="com.atguigu.bean"/> <!--推荐使用全类名 --> </typeAliases>
@Alias("emp")//为该类起另外别名,配置文件里的就失效了 public class Employee { private Integer id; private String empName; private String email; private Integer gender; private String loginAccount;
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 映射的类型 _byte byte _long long _short short _int int _integer int _double double _float float _boolean boolean string String byte Byte long Long short Short int Integer integer Integer double Double float Float boolean Boolean date Date decimal BigDecimal bigdecimal BigDecimal object Object map Map hashmap HashMap list List arraylist ArrayList collection Collection iterator Iterator 类型处理器 typeHandlers
<typeHandlers> <!--自定义好的类型处理器这么配置上就行了 --> <typeHandler handler=""/> </typeHandlers>
plugins插件
plugins插件是MyBatis中强大的机制
environments
<!--6.environments配置环境们 environment:配置一个具体的环境,都需要一个事务管理器和一个数据源 id:是当前环境的唯一表示 default:默认使用哪个环境 transactionManager:事务管理器 dataSource:数据源 后来的数据源和事务管理器都是Spring来做--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--配置连接池 --> <dataSource type="POOLED"> <property name="driver" value="${driverclass}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
databaseIdProvider:mybatis用来数据库移植的
<!--6.databaseIdProvider:mybatis用来数据库移植的 --> <databaseIdProvider type="DB_VENDOR"> <!--name="",数据库厂商标识,value,给这个标识起一个好用的名字 --> <property name="MySQL" value="mysql"/> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider>
映射文件
<!--如果能精确匹配就精确匹配,不能就模糊匹配 --> <select id="getEmpById" resultType="com.atguigu.bean.Employee" databaseId="mysql"> <!--sql语句不要写分号 --> select * from t_employee where id=#{id} </select> <select id="getEmpById" resultType="com.atguigu.bean.Employee" databaseId="oracle"> <!--sql语句不要写分号 --> <!--oracle语句--> </select> <select id="getEmpById" resultType="com.atguigu.bean.Employee" databaseId="db2"> <!--sql语句不要写分号 --> <!--db2语句--> </select>
mappers:写好的sql映射文件需要使用mapper注册进来
<!--7.写好的sql映射文件需要使用mapper注册进来 --> <mappers> <!-- resource:表示从类路径下找资源 class:直接引用接口的全类名 可以将xml放在和dao接口通目录下,而且文件名和接口名一致 url:可以从网络或者磁盘路径中引用 --> <mapper resource="EmployeeDao.xml"/> <!--注解方式 --> <mapper class="com.atguigu.dao.EmployeeDaoAnno"/> <!--resource、class两种方式可以配合使用,重要的dao写在配置中,不重要的可以写注解 --> <!--批量注册 --> <package name="com.atguigu.dao"/> </mappers>
注解java
public interface EmployeeDaoAnno { //按照员工id查询员工 @Select("select * from t_employee where id=#{id}") public Employee getEmpById(Integer id); //更新员工 @Update("UPDATE t_employee SET empname=#{empName},gender=#{gender},email=#{email} WHERE id=#{id}") public int updateEmployee(Employee employee); //删除员工 @Delete("DELETE FROM t_employee WHERE id=#{id}") public boolean deleteEmployee(Integer id); //插入员工 @Insert("INSERT INTO t_employee(empname,gender,email) VALUES(#{empName},#{gender},#{email})") public int insertEmployee(Employee employee); }
- SQL映射文件
- 增删改属性
主键自增
<!--让MyBatis自动的将自增id赋值传入的employee对象的id属性 useGeneratedKeys="true",原生的jdbc获取自增主键的方法 keyProperty="",将刚才自增的id封装给哪个属性 --> <insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_employee(empname,gender,email) VALUES(#{empName},#{gender},#{email}) </insert>
非主键自增的
<insert id="insertEmployee"> <!--order="BEFORE"表示执行下面sql语句之前执行里面sql语句,AFTER表示在之后 之后会将id值赋给插入的javaBean的对象 --> <selectKey order="BEFORE" resultType="Integer" keyProperty="id"> select max(id) from t_employee </selectKey> INSERT INTO t_employee(empname,gender,email) VALUES(#{empName},#{gender},#{email}) </insert>
查询属性
单个参数:基本类型取值:#{随便写},POJO则是#{属性名}
多个参数:
public Employee getEmpByIdAndEmpName(Integer id,String empName) 取值:#{参数名}是无效了; 可用:0,1(参数的索引)或者param1,param2(第几个参数paramN) 原因:只要传入了多个参数;mybatis会自动的将这些参数封装在一个map中; 封装时使用的key就是参数的索引和参数的第几个表示 Map<String,Object> map = new HashMap<>(); map.put("1",传入的值);map.put("2","传入的值"); #{key}就是从这个map中取值;
3)、@Param:为参数指定key;该注解是加在接口对应方法的参数前面的,我们以后也推荐这么做;
我们可以告诉mybatis,封装参数map的时候别乱来,使用我们指定的key传入了pojo(javaBean):取值:#{pojo的属性名}
传入了map:将多个要使用的参数封装起来,取值:#{key}
#{key/属性名}
#{key}取值的时候可以设置一些规则:如id=#{id,jdbcType=INT};
- 规则:javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression
- 只有jdbcType才可能是需要被指定的;默认不指定jdbcType;mysql没问题;oracle没问题;万一传入的数据是null;mysql插入null没问题,oracle会出现问题【oracle不知道null到底是什么类型;】
实际上在mybatis中:两种取值方式:#{属性名}和${属性名}
#{属性名}:是参数预编译的方式,参数的位置都是用?替代,参数后来都是预编译设置进去的;安全,不会有sql注入问题
id=#{id} and empname=#{empName}: select * from t_employee where id=? and empname=?
${属性名}:不是参数预编译,而是直接和sql语句进行拼串;不安全;
id=${id} and empname=#{empName}: select * from t_employee where id=1 and empname=?
sql语句只有参数位置是支持预编译的,而表位置是不支持预编译的,当需要动态传表名时,表明位置就可以用${},
一般都是使用#{};安全;在不支持参数预编译的位置要进行取值就使用${};
查询结果返回list
<!-- public List<Employee> getAllEmps(); --> <!-- resultType="":如果返回的是集合,写的是集合里面元素的类型 --> <select id="getAllEmps" resultType="com.atguigu.bean.Employee"> select * from t_employee </select>
@Test public void testGetAllEmps() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee数组对象 List<Employee> list=employeeDao.getAllEmps(); System.out.println(list); } finally{ openSession.close(); } }
查询结果返回Map
<!--public Map<String,Object> getEmpByIdReturnMap(Integer id); --> <select id="getEmpByIdReturnMap" resultType="map"> select * from t_employee where id=#{id} </select>
@Test public void testReturnMap() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee数组对象 Map<String,Object> map=employeeDao.getEmpByIdReturnMap(1); System.out.println(map); } finally{ openSession.close(); } }
查询结果返回多个对象的
<!-- public Map<Integer,Employee> getEmpsReturnMap(); --> <!--注意,返回结果的是集合的,需要将resultType设置为集合中的类型 --> <select id="getEmpsReturnMap" resultType="Employee"> select * from t_employee </select>
//返回的是所有Employee的Map集合 //@MapKey标注查出来的数据哪个属性作为map的key @MapKey("id") public Map<Integer,Employee> getEmpsReturnMap();
@Test public void testReturnMaps() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 EmployeeDao employeeDao=openSession.getMapper(EmployeeDao.class); //4.获取到employee数组对象 Map<Integer,Employee> map=employeeDao.getEmpsReturnMap(); System.out.println(map); } finally{ openSession.close(); } }
resultMap自定义封装规则
CatDao.xml
<?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.atguigu.dao.CatDao"> <!-- getCatById(Integer) resultType="com.atguigu.bean.Cat":使用默认规则;属性列名一一对应 resultMap="mycat":查出数据封装结果的时候,使用mycat自定义的规则 --> <select id="getCatById" resultMap="mycat"> select * from t_cat where id=#{id} </select> <!-- 自定义结果集(resultMap):自己定义每一列数据和javaBean的映射规则 type="":指定为哪个javaBean自定义封装规则;全类名 id="":唯一标识;让别名在后面引用 id cName cAge cgender 1 加菲猫 12 0 --> <resultMap type="com.atguigu.bean.Cat" id="mycat"> <!-- 指定主键列的对应规则; column="id":指定哪一列是主键列 property="":指定cat的哪个属性封装id这一列数据 --> <id property="id" column="id"/> <!-- 普通列 --> <result property="name" column="cName"/> <result property="age" column="cAge"/> <result property="gender" column="cgender"/> </resultMap> </mapper>
CatDao.java
public interface CatDao { public Cat getCatById(Integer id); }
Cat.java
package com.atguigu.bean; public class Cat { private Integer id; private String name; private Integer gender; private Integer age; /** * @return the id */ public Integer getId() { return id; } /** * @param id the id to set */ public void setId(Integer id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the gender */ public Integer getGender() { return gender; } /** * @param gender the gender to set */ public void setGender(Integer gender) { this.gender = gender; } /** * @return the age */ public Integer getAge() { return age; } /** * @param age the age to set */ public void setAge(Integer age) { this.age = age; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Cat [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + "]"; } }
Test.java
/** * 默认mybatis自动封装结果集; * 1)、按照列名和属性名一一对应的规则(不区分大小写); * 2)、如果不一一对应; * 1)、开启驼峰命名法(满足驼峰命名规则 aaa_bbb aaaBbb) * 2)、起别名: */ @Test public void test04() { SqlSession openSession = sqlSessionFactory.openSession(); try { CatDao mapper = openSession.getMapper(CatDao.class); Cat catById = mapper.getCatById(1); System.out.println(catById); } finally { openSession.close(); } }
联合查询
使用级联属性封装联合查询后的所有结果
Key.java
public class Key { private int id; private String keyName; private Lock lock; }
Lock.java
public class Lock { private int id; private String lockName; }
KeyDao.java
public interface KeyDao { public Key getKeyById(Integer id); }
KeyDao.xml
<!-- getKeyById(Integer) --> <!-- private Integer id;//钥匙的id private String keyName;//钥匙的名 private Lock lock;//当前钥匙能开哪个锁; id keyname lockid lid lockName --> <select id="getKeyById" resultMap="mykey"> select k.id,k.`keyname`,k.`lockid`, l.`id` lid,l.`lockName` from t_key k left join t_lock l on k.`lockid`=l.`id` where k.`id`=#{id} </select> <!-- 自定义封装规则:使用级联属性封装联合查询出的结果 --> <resultMap type="com.atguigu.bean.Key" id="mykey"> <id property="id" column="id"/> <result property="keyName" column="keyname"/> <result property="lock.id" column="lid"/> <result property="lock.lockName" column="lockName"/> </resultMap>
MyBatis推荐的方式
<!-- mybatis推荐的 <association property=""></association>--> <resultMap type="com.atguigu.bean.Key" id="mykey"> <id property="id" column="id"/> <result property="keyName" column="keyname"/> <!-- 接下来的属性是一个对象,自定义这个对象的封装规则;使用association;表示联合了一个对象 --> <!-- javaType:指定这个属性的类型 --> <association property="lock" javaType="com.atguigu.bean.Lock"> <!-- 定义lock属性对应的这个Lock对象如何封装 --> <id property="id" column="lid"/> <result property="lockName" column="lockName"/> </association> </resultMap>
collection定义集合封装类型
<?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.atguigu.dao.LockDao"> <!-- public Lock getLockById(Integer id); --> <select id="getLockById" resultMap="mylock"> select l.*,k.id kid,k.`keyname`,k.`lockid` from t_lock l left join t_key k on l.`id`=k.`lockid` where l.id=#{id} </select> <!-- private Integer id; private String lockName; //查询锁子的时候把所有的钥匙也查出来 private List<Key> keys; id lockName kid keyname lockid 3 303办公室的锁子 3 303钥匙1 3 3 303办公室的锁子 4 303钥匙2 3 3 303办公室的锁子 5 303钥匙3 3 --> <resultMap type="com.atguigu.bean.Lock" id="mylock"> <id property="id" column="id"/> <result property="lockName" column="lockName"/> <!-- collection:定义集合元素的封装 property="":指定哪个属性是集合属性 javaType:指定对象类型;association ofType="":指定集合里面元素的类型 --> <collection property="keys" ofType="com.atguigu.bean.Key"> <!-- 标签体中指定集合中这个元素的封装规则 --> <id property="id" column="kid"/> <result property="keyName" column="keyname"/> </collection> </resultMap> </mapper>
public interface LockDao { //查锁子的时候将所有钥匙也查出来 public Lock getLockById(Integer id); }
@Test public void testLockDao() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 LockDao lockDao=openSession.getMapper(LockDao.class); Lock lock=lockDao.getLockById(3); System.out.println(lock); } finally{ openSession.close(); } }
使用select实现分布查询
<select id="getKeyByIdSimple" resultMap="mykey02"> select * from t_key where id=#{id} </select> <resultMap type="com.atguigu.bean.Key" id="mykey02"> <id property="id" column="id"/> <result property="keyName" column="keyname"/> <!--告诉mybatis自己去调用一个查询查锁子 select="":指定一个查询sql的唯一标识;mybatis自动调用指定的sql将查出的lock封装进来 public Lock getLockByIdSimple(Integer id);需要传入锁子id 告诉mybatis把哪一列的值传递过去 column:指定将哪一列的数据传递过去 --> <association property="lock" select="LockDao.getLockById" column="lockid" fetchType="lazy"></association> </resultMap>
@Test public void testKeyDaoSelect() throws IOException { //1.根据全局配置文件创建出一个SqlSessionFactory init(); //2.获取和数据库的一次会话,getConnection(); SqlSession openSession=sqlSessionFactory.openSession(true); try { //3.使用sqlSession操作数据库,获取到dao接口的实现 KeyDao keyDao=openSession.getMapper(KeyDao.class); Key key=keyDao.getKeyByIdSimple(1); System.out.println(); } finally{ openSession.close(); } }
如果要按需加载和延迟加载
<settings> <!-- 开启延迟加载开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 开启属性按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>
动态sql
if判断与trim
<!-- if:判断 --> <!--public List<Teacher> getTeacherByCondition(Teacher teacher); --> <select id="getTeacherByCondition" resultMap="teacherMap"> select * from t_teacher <!-- test="":编写判断条件 id!=null:取出传入的javaBean属性中的id的值,判断其是否为空 --> <!-- where可以帮我们去除掉前面的and; --> <!-- trim:截取字符串 prefix="":前缀;为我们下面的sql整体添加一个前缀 prefixOverrides="": 取出整体字符串前面多余的字符 suffix="":为整体添加一个后缀 suffixOverrides="":后面哪个多了可以去掉; --> <!-- 我们的查询条件就放在where标签中;每个and写在前面, where帮我们自动取出前面多余的and --> <trim prefix="where" prefixOverrides="and" suffixOverrides="and"> <if test="id!=null"> id > #{id} and </if> <!-- 空串 "" and; && or: ||; if():传入非常强大的判断条件; 方法、静态方法、构造器。xxx 在mybatis中,传入的参数可以用来做判断; 额外还有两个东西; _parameter:代表传入来的参数; 1)、传入了单个参数:_parameter就代表这个参数 2)、传入了多个参数:_parameter就代表多个参数集合起来的map _databaseId:代表当前环境 如果配置了databaseIdProvider:_databaseId就有值 --> <if test="name!=null && !name.equals("")"> teacherName like #{name} and </if> <if test="birth!=null"> birth_date < #{birth} and </if> </trim> </select>
OGNL对象导航图语言
OGNL表达式;对象导航图语言 Person ===lastName ===email ===Address ===city ===province ===Street ===adminName ===info ===perCount
foreach
<!-- public List<Teacher> getTeacherByIdIn(List<Integer> ids); --> <select id="getTeacherByIdIn" resultMap="teacherMap"> SELECT * FROM t_teacher WHERE id IN <!-- 帮我们遍历集合的; collection="":指定要遍历的集合的key close="":以什么结束 index="i":索引; 如果遍历的是一个list; index:指定的变量保存了当前索引 item:保存当前遍历的元素的值 如果遍历的是一个map: index:指定的变量就是保存了当前遍历的元素的key item:就是保存当前遍历的元素的值 item="变量名":每次遍历出的元素起一个变量名方便引用 open="":以什么开始 separator="":每次遍历的元素的分隔符 (#{id_item},#{id_item},#{id_item} --> <if test="ids.size >0"> <foreach collection="ids" item="id_item" separator="," open="(" close=")"> #{id_item} </foreach> </if> </select>
choose选择
<!--if与when的区别:if是每个条件都会执行一遍,而when则按顺序只执行第一个满足的条件 --> <!--public List<Teacher> getTeacherByConditionChoose(Teacher teacher); --> <select id="getTeacherByConditionChoose" resultMap="teacherMap"> select * from t_teacher <where> <choose> <when test="id!=null"> id=#{id} </when> <when test="name!=null and !name.equals("")"> teacherName=#{name} </when> <when test="birth!=null"> birth_date = #{birth} </when> <otherwise> 1=1 </otherwise> </choose> </where> </select>
update中的set
<!-- public int updateTeacher(Teacher teacher); --> <update id="updateTeacher"> UPDATE t_teacher <set> <if test="name!=null and !name.equals("")"> teacherName=#{name}, </if> <if test="course!=null and !course.equals("")"> class_name=#{course}, </if> <if test="address!=null and !address.equals("")"> address=#{address}, </if> <if test="birth!=null"> birth_date=#{birth} </if> </set> <where> id=#{id} </where> </update>
抽取sql
<!--抽取可重用的sql语句 --> <sql id="selectSql">select * from t_teacher</sql> <!--public Teacher getTeacherById(Integer id); --> <select id="getTeacherById" resultMap="teacherMap"> <include refid="selectSql"></include> where id=#{id} </select>
- 缓存
暂时的存储一些数据;加快系统的查询速度…
一级缓存:是线程级别的缓存,本地缓存,SqlSession级别的缓存,默认是存在的;
机制:只要之前查询过的数据,mybatis就会保存在一个缓存中(Map);下次获取直接从缓存中拿;
一级缓存失效的几种情况:
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存
会生成一个很长的key
二级缓存:全局范围的缓存,除过当前线程,SqlSession能用外其他也可以使用,一般缓存的bean要实现序列化接口;namespace级别的缓存;
全局配置开启二级缓存
<!-- 开启全局缓存开关; --> <setting name="cacheEnabled" value="true"/>
配置某个dao.xml文件,让其使用二级缓存
<cache></cache>
缓存的原理
缓存的原理图
缓存的顺序(可参照原理图)
- 不会出现一级缓存和二级缓存中有同一个数据,在二级缓存中,一级缓存关闭了就有了;一级缓存中:二级缓存中没有此数据,就会看一级缓存,一级缓存没有去查数据库;数据库的查询后的结果放在一级缓存中了;
- 任何时候都是先看二级缓存、再看一级缓存,如果大家都没有就去查询数据库;
- 查询顺序可简单理解为:二级缓存-》一级缓存-》数据库
缓存的配置
- 全局setting的cacheEnable:配置二级缓存的开关。一级缓存一直是打开的。
- select标签的useCache属性:配置这个select是否使用二级缓存。一级缓存一直是使用的
- sql标签的flushCache属性:增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。
- sqlSession.clearCache():只是用来清除一级缓存。
- 当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
整合第三方缓存包的顺序
导入第三方包
ehcache-core-2.6.8.jar(ehcache核心包) mybatis-ehcache-1.0.3.jar(ehcache的整合包) slf4j-api-1.7.21.jar slf4j-log4j12-1.7.21.jar
ehcache要工作有一个配置文件;
在mapper.xml中配置使用自定义的缓存<?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:\44\ehcache" /> <defaultCache maxElementsInMemory="1" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache> <!-- 属性说明: l diskStore:指定数据在磁盘中的存储位置。 l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略 以下属性是必须的: l maxElementsInMemory - 在内存中缓存的element的最大数目 l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大 l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断 l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 以下属性是可选的: l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大 l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区. l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) -->
在mapper.xml中配置使用自定义的缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
别的dao还要用这个缓存,缓存引用cache-ref
<!-- 和别的dao共用一块缓存--> <cache-ref namespace="com.atguigu.dao.TeacherDao"/>
- MBG逆向工程
逆向工程:根据数据表table,逆向分析数据表,自动生成javaBean—BookDao—dao.xml—xxx
MBG:MyBatis Generator:代码生成器;MyBatis官方提供的代码生成器;帮我们逆向生成;
使用步骤
导包:mbg的核心包
编写mbg.xml配置文件
<?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> <!-- MyBatis3Simple:基础班CRUD MyBatis3:复杂版CRUD --> <context id="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressAllComments" value="true"/> </commentGenerator> <!-- jdbcConnection:指导连接到哪个数据库 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis_0325" userId="root" password="123456"> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- javaModelGenerator:生成pojo targetPackage:生成的pojo放在哪个包 targetProject:放在哪个工程下 --> <javaModelGenerator targetPackage="com.atguigu.bean" targetProject=".\src"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!--sqlMapGenerator:sql映射文件生成器;指定xml生成的地方 --> <sqlMapGenerator targetPackage="com.atguigu.dao" targetProject=".\conf"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- javaClientGenerator:dao接口生成的地方 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.dao" targetProject=".\src"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- table:指定要逆向生成哪个数据表 tableName="t_cat":表名 domainObjectName="":这个表对应的对象名 --> <table tableName="t_cat" domainObjectName="Cat"></table> <table tableName="t_employee" domainObjectName="Employee"></table> <table tableName="t_teacher" domainObjectName="Teacher"></table> </context> </generatorConfiguration>
运行代码生成
package com.atguigu.test; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.exception.XMLParserException; import org.mybatis.generator.internal.DefaultShellCallback; public class MBGTest { public static void main(String[] args) throws Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("mbg.xml"); 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); System.out.println("生成ok了!"); } }
测试复杂查询
package com.atguigu.test; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import com.atguigu.bean.Teacher; import com.atguigu.bean.TeacherExample; import com.atguigu.bean.TeacherExample.Criteria; import com.atguigu.dao.TeacherMapper; public class MyBatisTest { // 工厂一个 SqlSessionFactory sqlSessionFactory; @Test public void test02(){ SqlSession sqlSession = sqlSessionFactory.openSession(); //1、测试 TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); List<Teacher> teachers = new ArrayList<Teacher>(); for (int i = 0; i < 1000; i++) { Teacher teacher = new Teacher(); teacher.setTeachername(UUID.randomUUID().toString().substring(0, 5)); teacher.setClassName(UUID.randomUUID().toString().substring(0, 5)); teachers.add(teacher); } System.out.println("批量保存....."); mapper.insertBatch(teachers); sqlSession.commit(); sqlSession.close(); } /** * 测试代码生成器 * @throws IOException */ @Test public void test01(){ SqlSession sqlSession = sqlSessionFactory.openSession(); //1、测试 TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); //2、测试查询所有teacher List<Teacher> list = mapper.selectByExample(null); for (Teacher teacher : list) { System.out.println(teacher); } //3、带复杂条件的查询 //select * from t_teacher id=? and teacherName like ? //封装查询条件的 TeacherExample example = new TeacherExample(); example.setOrderByClause("id DESC"); //1、使用example创建一个Criteria(查询准则) Criteria criteria = example.createCriteria(); criteria.andIdEqualTo(1); criteria.andTeachernameLike("%a%"); System.out.println("======================"); List<Teacher> list2 = mapper.selectByExample(example); for (Teacher teacher : list2) { System.out.println(teacher); } /** * 多个复杂条件 * select * from t_teacher where (id=? and teacherName like ?) or (address like ? and birth bet) */ TeacherExample example2 = new TeacherExample(); //一个Criteria能封装一整个条件 Criteria criteria2 = example2.createCriteria(); criteria2.andIdGreaterThan(1); criteria2.andTeachernameLike("%a%"); //创建第二个查询条件 Criteria criteria3 = example2.createCriteria(); criteria3.andAddressLike("%%"); criteria3.andBirthDateBetween(new Date(), new Date()); example2.or(criteria3); System.out.println("=======-=-=-=-=-=-=-"); mapper.selectByExample(example2); } @Before public void initSqlSessionFactory() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }