转载请务必注明出处,原创不易!java
一、 数据库连接建立、释放频繁形成系统资源浪费从而影响系统性能,若是使用数据库连接池可解决此问题。mysql
解决:在SqlMapConfig.xml中配置数据连接池,使用链接池管理数据库连接。git
二、 Sql语句写在代码中形成代码不易维护,实际应用sql变化的可能较大,sql变更须要改变java代码。程序员
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。github
三、 向sql语句传参数麻烦,由于sql语句的where条件不必定,可能多也可能少,占位符须要和参数一一对应。spring
解决:Mybatis自动将java对象映射至sql语句,经过statement中的parameterType定义输入参数的类型。sql
四、 对结果集解析麻烦,sql变化致使解析代码变化,且解析前须要遍历,若是能将数据库记录封装成pojo对象解析比较方便。数据库
解决:Mybatis自动将sql执行结果映射至java对象,经过statement中的resultType定义输出结果的类型。编程
Mybatis和hibernate不一样,它不彻底是一个ORM框架,由于MyBatis须要程序员本身编写Sql语句,不过mybatis能够经过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。数组
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,很是适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,由于这类软件需求变化频繁,一但需求变化要求成果输出迅速。可是灵活的前提是mybatis没法作到数据库无关性,若是须要实现支持多种数据库的软件则须要自定义多套sql映射文件,工做量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)若是用hibernate开发能够节省不少代码,提升效率。可是Hibernate的学习门槛高,要精通门槛更高,并且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate须要具备很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能作出维护性、扩展性良好的软件架构都是好架构,因此框架只有适合才是最好。
原始 dao 开发方法(程序须要编写 dao 接口和 dao 实现类)(掌握)
Mybatis 的 mapper 接口(至关于 dao 接口)代理开发方法(掌握)
将下边的功能实现Dao:
根据用户id查询一个用户信息
根据用户名称模糊查询用户信息列表
添加用户信息
Mybatis 配置文件 SqlMapConfig.xml
SqlSession 中封装了对数据库的操做,如:查询、插入、更新、删除等。
经过 SqlSessionFactory 建立 SqlSession,而 SqlSessionFactory 是经过 SqlSessionFactoryBuilder 进行建立。
SqlSessionFactoryBuilder 用于建立 SqlSessionFacoty,SqlSessionFacoty 一旦建立完成就不须要SqlSessionFactoryBuilder 了,由于 SqlSession 是经过 SqlSessionFactory 生产,因此能够将SqlSessionFactoryBuilder 当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
SqlSessionFactory 是一个接口,接口中定义了 openSession 的不一样重载方法,SqlSessionFactory 的最佳使用范围是整个应用运行期间,一旦建立后能够重复使用,一般以单例模式管理 SqlSessionFactory。
SqlSession 是一个面向用户的接口, sqlSession 中定义了数据库操做,默认使用 DefaultSqlSession 实现类。
执行过程以下:
1)、 加载数据源等配置信息
Environment environment = configuration.getEnvironment();
2)、 建立数据库连接
3)、 建立事务对象
4)、 建立Executor,SqlSession 全部操做都是经过 Executor 完成,mybatis 源码以下:
if (ExecutorType.BATCH == executorType) { executor = newBatchExecutor(this, transaction); } elseif (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); }
5)、 SqlSession的实现类即 DefaultSqlSession,此对象中对操做数据库实质上用的是 Executor
每一个线程都应该有它本身的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。所以最佳的范围是请求或方法范围(定义局部变量使用)。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。 打开一个SqlSession;使用完毕就要关闭它。一般把这个关闭操做放到 finally 块中以确保每次都能执行关闭。以下:
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
须要程序员编写 Dao 接口和 Dao 实现类;
须要在 Dao 实现类中注入 SqlsessionFactory ,在方法体内经过 SqlsessionFactory 建立 Sqlsession。
public interface UserDao //dao接口,用户管理 { //根据id查询用户信息 public User findUserById(int id) throws Exception; //添加用户信息 public void addUser(User user) throws Exception; //删除用户信息 public void deleteUser(int id) throws Exception; }
public class UserDaoImpl implements UserDao //dao接口实现类 { //须要在 Dao 实现类中注入 SqlsessionFactory //这里经过构造方法注入 private SqlSessionFactory sqlSessionFactory; public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public User findUserById(int id) throws Exception { //在方法体内经过 SqlsessionFactory 建立 Sqlsession SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("test.findUserById", id); sqlSession.close(); return user; } @Override public void insertUser(User user) throws Exception { //在方法体内经过 SqlsessionFactory 建立 Sqlsession SqlSession sqlSession = sqlSessionFactory.openSession(); //执行插入的操做 sqlSession.insert("test.insetrUser", user); //提交事务 sqlSession.commit(); //释放资源 sqlSession.close(); } @Override public void deleteUser(int id) throws Exception { //在方法体内经过 SqlsessionFactory 建立 Sqlsession SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.delete("test.deleteUserById", id); //提交事务 sqlSession.commit(); sqlSession.close(); } }
public class UserDaoImplTest { private SqlSessionFactory sqlSessionFactory; //此方法是在 testFindUserById 方法以前执行的 @Before public void setup() throws Exception { //建立sqlSessionFactory //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //获得配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //建立会话工厂,传入Mybatis的配置文件信息 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() throws Exception { //建立UserDao的对象 UserDao userDao = new UserDaoImpl(sqlSessionFactory); //调用UserDao方法 User user = userDao.findUserById(1); System.out.println(user); } }
经过id查询用户信息测试结果以下:(其余的能够本身在写测试代码,原理相似)
原始Dao开发中存在如下问题:
Dao方法体存在重复代码:经过 SqlSessionFactory 建立 SqlSession,调用 SqlSession 的数据库操做方法
调用 sqlSession 的数据库操做方法须要指定 statement 的i d,这里存在硬编码,不得于开发维护。
调用 sqlSession 的数据库操做方法时传入的变量,因为 sqlsession 方法使用泛型,即便变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
程序员须要编写 mapper.xml 映射文件
只须要程序员编写Mapper接口(至关于Dao接口),需遵循一些开发规范,mybatis 能够自动生成 mapper 接口类代理对象。
开发规范:
在 mapper.xml 中 namespace 等于 mapper 接口地址
<mapper namespace="cn.zhisheng.mybatis.mapper.UserMapper"></mapper>
在 xxxmapper.java 接口中的方法名要与 xxxMapper.xml 中 statement 的 id 一致。
在 xxxmapper.java 接口中的输入参数类型要与 xxxMapper.xml 中 statement 的 parameterType 指定的参数类型一致。
在 xxxmapper.java 接口中的返回值类型要与 xxxMapper.xml 中 statement 的 resultType 指定的类型一致。
UserMapper.java
//根据id查询用户信息 public User findUserById(int id) throws Exception;
UserMapper.xml
<select id="findUserById" parameterType="int" resultType="cn.zhisheng.mybatis.po.User"> select * from user where id = #{1} </select>
以上的开发规范主要是对下边的代码进行统一的生成:
User user = sqlSession.selectOne("test.findUserById", id); sqlSession.insert("test.insetrUser", user); sqlSession.delete("test.deleteUserById", id); List<User> list = sqlSession.selectList("test.findUserByName", username);
测试以前记得在 SqlMapConfig.xml 文件中添加加载映射文件 UserMapper.xml:
<mapper resource="mapper/UserMapper.xml"/>
测试代码:
public class UserMapperTest { private SqlSessionFactory sqlSessionFactory; //此方法是在 testFindUserById 方法以前执行的 @Before public void setup() throws Exception { //建立sqlSessionFactory //Mybatis 配置文件 String resource = "SqlMapConfig.xml"; //获得配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); //建立会话工厂,传入Mybatis的配置文件信息 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); //建立usermapper对象,mybatis自动生成代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //调用UserMapper的方法 User user = userMapper.findUserById(1); System.out.println(user); } }
经过id查询用户信息测试结果以下:(其余的请本身根据上下文写测试代码,或者去看我 Github-Mybatis学习笔记 上看我这个项目的所有代码)
经过姓名查询用户信息:
若是 mapper 方法返回单个 pojo 对象(非集合对象),代理对象内部经过 selectOne 查询数据库
若是 mapper 方法返回集合对象,代理对象内部经过 selectList 查询数据库
mapper 接口方法参数只能有一个,系统是否不利于维护?
系统框架中,dao层的代码是被业务层公用的。
即便 mapper 接口只有一个参数,可使用包装类型的 pojo 知足不一样的业务方法的需求。
注意:持久层方法的参数能够包装类型、map.... ,service方法中不建议使用包装类型。(不利于业务层的可扩展性)
Mybatis 的全局配置变量,配置内容和顺序以下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
需求:将数据库链接参数单独配置在 db.properties 中,只须要在 SqlMapConfig.xml 中加载该配置文件 db.properties 的属性值。在 SqlMapConfig.xml 中就不须要直接对数据库的链接参数进行硬编码了。方便之后对参数进行统一的管理,其余的xml文件能够引用该 db.properties 。
db.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8 jdbc.username=root jdbc.password=root
那么 SqlMapConfig.xml 中的配置变成以下:
<!--加载配置文件--> <properties resource="db.properties"></properties> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理,事务由 Mybatis 控制--> <transactionManager type="JDBC" /> <!-- 数据库链接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments>
配置完成后咱们测试一下是否可以和刚才同样的可以成功呢?那么我就先在db.properties中把数据库密码故意改错,看是不是正确的?不出意外的话是会报错的。
注意: MyBatis 将按照下面的顺序来加载属性:
在 properties 元素体内定义的属性首先被读取。
而后会读取 properties 元素中 resource 或 url 加载的属性,它会覆盖已读取的同名属性。
最后读取 parameterType 传递的属性,它会覆盖已读取的同名属性。
所以,经过parameterType传递的属性具备最高优先级,resource或 url 加载的属性次之,最低优先级的是 properties 元素体内定义的属性。
建议:
不要在 properties 元素体内添加任何属性值,只将属性值定义在 db.properties 文件之中。
在 db.properties 文件之中定义的属性名要有必定的特殊性。如 xxx.xxx.xxx
Mybatis 框架在运行时能够调整一些运行参数
好比:开启二级缓存、开启延迟加载。。。
需求:
在mapper.xml中,定义不少的statement,statement须要parameterType指定输入参数的类型、须要resultType指定输出结果的映射类型。
若是在指定类型时输入类型全路径,不方便进行开发,能够针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中经过别名定义,方便开发。
Mybatis支持的别名:
别名 | 映射的类型 |
---|---|
_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 |
自定义别名:
在 SqlMapConfig.xml 中配置:(设置别名)
<typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="cn.zhisheng.mybatis.po.User"/> <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写均可以) --> <package name="cn.zhisheng.mybatis.po"/> <package name="其它包"/> </typeAliases>
在 UserMapper.xml 中引用别名:( resultType 为 user )
<select id="findUserById" parameterType="int" resultType="user"> select * from user where id = #{id} </select>
测试结果:
mybatis中经过typeHandlers完成jdbc类型和java类型的转换。
一般状况下,mybatis提供的类型处理器知足平常须要,不须要自定义.
mybatis支持类型处理器:
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | Boolean,boolean | 任何兼容的布尔值 |
ByteTypeHandler | Byte,byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | Short,short | 任何兼容的数字或短整型 |
IntegerTypeHandler | Integer,int | 任何兼容的数字和整型 |
LongTypeHandler | Long,long | 任何兼容的数字或长整型 |
FloatTypeHandler | Float,float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | Double,double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | String | CHAR和VARCHAR类型 |
ClobTypeHandler | String | CLOB和LONGVARCHAR类型 |
NStringTypeHandler | String | NVARCHAR和NCHAR类型 |
NClobTypeHandler | String | NCLOB类型 |
ByteArrayTypeHandler | byte[] | 任何兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB和LONGVARBINARY类型 |
DateTypeHandler | Date(java.util) | TIMESTAMP类型 |
DateOnlyTypeHandler | Date(java.util) | DATE类型 |
TimeOnlyTypeHandler | Date(java.util) | TIME类型 |
SqlTimestampTypeHandler | Timestamp(java.sql) | TIMESTAMP类型 |
SqlDateTypeHandler | Date(java.sql) | DATE类型 |
SqlTimeTypeHandler | Time(java.sql) | TIME类型 |
ObjectTypeHandler | 任意 | 其余或未指定类型 |
EnumTypeHandler | Enumeration类型 | VARCHAR-任何兼容的字符串类型,做为代码存储(而不是索引)。 |
<mapper resource=" " />
使用相对于类路径的资源,如:<mapper resource="sqlmap/User.xml" />
<mapper url=" " />
使用彻底限定路径
如:<mapper url="file://D:workspacemybatisconfigsqlmapUser.xml" />
<mapper class=" " />
使用 mapper 接口类路径
如:<mapper class="cn.zhisheng.mybatis.mapper.UserMapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
<mapper name=" " />
注册指定包下的全部mapper接口
如:<package name="cn.zhisheng.mybatis.mapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
Mapper.xml映射文件中定义了操做数据库的sql,每一个sql是一个statement,映射文件是mybatis的核心。
经过 parameterType 指定输入参数的类型,类型能够是简单类型、hashmap、pojo的包装类型。
传递 pojo 包装对象 (重点)
开发中经过pojo传递查询条件 ,查询条件是综合的查询条件,不只包括用户查询条件还包括其它的查询条件(好比将用户购买商品信息也做为查询条件),这时可使用包装对象传递输入参数。
定义包装对象
定义包装对象将查询条件(pojo)以类组合的方式包装起来。
UserQueryVo.java
public class UserQueryVo //用户包装类型 { //在这里包装所须要的查询条件 //用户查询条件 private UserCustom userCustom; public UserCustom getUserCustom() { return userCustom; } public void setUserCustom(UserCustom userCustom) { this.userCustom = userCustom; } //还能够包装其余的查询条件,好比订单、商品 }
UserCustomer.java
public class UserCustom extends User //用户的扩展类 { //能够扩展用户的信息 }
UserMapper.xml
文件
<!--用户信息综合查询 #{userCustom.sex} :取出pojo包装对象中的性别值 #{userCustom.username} :取出pojo包装对象中的用户名称 --> <select id="findUserList" parameterType="cn.zhisheng.mybatis.po.UserQueryVo" resultType="cn.zhisheng.mybatis.po.UserCustom"> select * from user where user.sex = #{userCustom.sex} and user.username like '%${userCustom.username}%' </select>
UserMapper.java
//用户信息综合查询 public List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;
测试代码
//测试用户信息综合查询 @Test public void testFindUserList() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); //建立usermapper对象,mybatis自动生成代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //建立包装对象,设置查询条件 UserQueryVo userQueryVo = new UserQueryVo(); UserCustom userCustom = new UserCustom(); userCustom.setSex("男"); userCustom.setUsername("张小明"); userQueryVo.setUserCustom(userCustom); //调用UserMapper的方法 List<UserCustom> list = userMapper.findUserList(userQueryVo); System.out.println(list); }
测试结果
resultType
使用 resultType 进行输出映射,只有查询出来的列名和 pojo 中的属性名一致,该列才能够映射成功。
若是查询出来的列名和 pojo 中的属性名所有不一致,没有建立 pojo 对象。
只要查询出来的列名和 pojo 中的属性有一个一致,就会建立 pojo 对象。
需求:用户信息综合查询列表总数,经过查询总数和上边用户综合查询列表才能够实现分页
实现:
<!--用户信息综合查询总数 parameterType:指定输入的类型和findUserList同样 resultType:输出结果类型为 int --> <select id="findUserCount" parameterType="cn.zhisheng.mybatis.po.UserQueryVo" resultType="int"> select count(*) from user where user.sex = #{userCustom.sex} and user.username like '%${userCustom.username}%' </select>
//用户信息综合查询总数 public int findUserCount(UserQueryVo userQueryVo) throws Exception;
//测试用户信息综合查询总数 @Test public void testFindUserCount() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); //建立usermapper对象,mybatis自动生成代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //建立包装对象,设置查询条件 UserQueryVo userQueryVo = new UserQueryVo(); UserCustom userCustom = new UserCustom(); userCustom.setSex("男"); userCustom.setUsername("张小明"); userQueryVo.setUserCustom(userCustom); //调用UserMapper的方法 System.out.println(userMapper.findUserCount(userQueryVo)); }
注意:查询出来的结果集只有一行且一列,可使用简单类型进行输出映射。
输出pojo对象和pojo列表
不论是输出的pojo单个对象仍是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是同样的。
在mapper.java指定的方法返回值类型不同:
一、输出单个pojo对象,方法返回值是单个对象类型
//根据id查询用户信息 public User findUserById(int id) throws Exception;
二、输出pojo对象list,方法返回值是List<Pojo>
//根据用户名查询用户信息 public List<User> findUserByUsername(String userName) throws Exception;
resultType总结:
输出pojo对象和输出pojo列表在sql中定义的resultType是同样的。
返回单个pojo对象要保证sql查询出来的结果集为单条,内部使用session.selectOne方法调用,mapper接口使用pojo对象做为方法返回值。
返回pojo列表表示查询出来的结果集可能为多条,内部使用session.selectList方法,mapper接口使用List<pojo>对象做为方法返回值。
resultMap
resultType 能够指定 pojo 将查询结果映射为 pojo,但须要 pojo 的属性名和 sql 查询的列名一致方可映射成功。
若是sql查询字段名和pojo的属性名不一致,能够经过resultMap将字段名和属性名做一个对应关系 ,resultMap实质上还须要将查询结果映射到pojo对象中。
resultMap能够实现将查询结果映射为复杂类型的pojo,好比在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
使用方法:
一、定义 resultMap
二、使用 resultMap 做为 statement 的输出映射类型
将下面的 sql 使用 User 完成映射
select id id_, username username_ from user where id = #{value}
User 类中属性名和上边查询的列名不一致。
因此须要:
一、定义 resultMap
<!--定义 resultMap 将select id id_, username username_ from user where id = #{value} 和User类中的属性作一个映射关系 type: resultMap最终映射的java对象类型 id:对resultMap的惟一标识 --> <resultMap id="userResultMap" type="user"> <!--id表示查询结果中的惟一标识 column:查询出来的列名 property:type指定pojo的属性名 最终resultMap对column和property作一个映射关系(对应关系) --> <id column="id_" property="id"/> <!--result: 对普通结果映射定义 column:查询出来的列名 property:type指定pojo的属性名 最终resultMap对column和property作一个映射关系(对应关系) --> <result column="username_" property="username"/> </resultMap>
二、使用 resultMap 做为 statement 的输出映射类型
<!--使用 resultMap 做为输出映射类型 resultMap="userResultMap":其中的userResultMap就是咱们刚才定义的 resultMap 的id值,若是这个resultMap在其余的mapper文件中,前边须加上namespace --> <select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap"> select id id_, username username_ from user where id = #{value} </select>
三、UserMapper.java
//根据id查询用户信息,使用 resultMap 输出 public User findUserByIdResultMap(int id) throws Exception;
四、测试
//测试根据id查询用户信息,使用 resultMap 输出 @Test public void testFindUserByIdResultMap() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); //建立usermapper对象,mybatis自动生成代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //调用UserMapper的方法 User user = userMapper.findUserByIdResultMap(1); System.out.println(user); }
五、测试结果
经过mybatis提供的各类标签方法实现动态拼接sql。
需求:
用户信息综合查询列表和用户信息查询列表总数这两个 statement的定义使用动态sql。
对查询条件进行判断,若是输入的参数不为空才进行查询条件拼接。
UserMapper.xml
(findUserList的配置以下,那么findUserCount的也是同样的,这里就不所有写出来了)
<select id="findUserList" parameterType="cn.zhisheng.mybatis.po.UserQueryVo" resultType="cn.zhisheng.mybatis.po.UserCustom"> select * from user <!--where能够自动的去掉条件中的第一个and--> <where> <if test="userCustom != null"> <if test="userCustom.sex != null and userCustom.sex != ''"> and user.sex = #{userCustom.sex} </if> <if test="userCustom.username != null"> and user.username like '%${userCustom.username}%' </if> </if> </where> </select>
测试代码:由于设置了动态的sql,若是不设置某个值,那么条件就不会拼接在sql上
因此咱们就注释掉设置username的语句
//userCustom.setUsername("张小明");
测试结果:
经过上面的其实看到在 where sql语句中有不少重复代码,咱们能够将其抽取出来,组成一个sql片断,其余的statement就能够引用这个sql片断,利于系统的开发。
这里咱们就拿上边sql 中的where定义一个sq片断以下:
<!--sql片断 id:惟一标识 经验:是基于单表来定义sql片断,这样的话sql片断的可重用性才高 通常不包含where --> <sql id="query_user_where"> <if test="userCustom != null"> <if test="userCustom.sex != null and userCustom.sex != ''"> and user.sex = #{userCustom.sex} </if> <if test="userCustom.username != null"> and user.username like '%${userCustom.username}%' </if> </if> </sql>
那么咱们该怎样引用这个sql片断呢?以下:
select * from user <where> <!--refid: 指定sql片断的id,若是是写在其余的mapper文件中,则须要在前面加上namespace--> <include refid="query_user_where"/> </where>
测试的话仍是那样了,就不继续说了,前面已经说了不少了。
向sql传递数组或List,mybatis使用foreach解析
需求:
在用户查询列表和查询总数的statement中增长多个id输入查询。
sql语句以下:
SELECT * FROM USER WHERE id=1 OR id=10 ORid=16 或者 SELECT * FROM USER WHERE id IN(1,10,16)
在输入参数类型中添加 List<Integer> ids 传入多个 id
public class UserQueryVo //用户包装类型 { //传入多个id private List<Integer> ids; }
修改 UserMapper.xml文件
WHERE id=1 OR id=10 OR id=16
在查询条件中,查询条件定义成一个sql片断,须要修改sql片断。
<if test="ids!=null"> <!-- 使用 foreach遍历传入ids collection:指定输入 对象中集合属性 item:每一个遍历生成对象中 open:开始遍历时拼接的串 close:结束遍历时拼接的串 separator:遍历的两个对象中须要拼接的串 --> <!-- 使用实现下边的sql拼接: AND (id=1 OR id=10 OR id=16) --> <foreach collection="ids" item="user_id" open="AND (" close=")" separator="or"> <!-- 每一个遍历须要拼接的串 --> id=#{user_id} </foreach> <!-- 实现 “ and id IN(1,10,16)”拼接 --> <!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=","> 每一个遍历须要拼接的串 #{user_id} </foreach> --> </if>
测试代码:
//传入多个id List<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(10); ids.add(16); //将ids传入statement中 userQueryVo.setIds(ids);
期待后续的文章吧!