开发人员在使用JDBC框架或者其他类似的框架进行数据库开发时,通常都要根据需求去手动拼接SQL,这样非常麻烦,而myBatis提供了对SQL语句动态组装的功能,恰好解决了这一问题。
一,动态SQL中的元素
动态SQL是MyBatis的强大特性之一,MyBatis 3后采用了基于OGNL的表达式来完成动态SQL,
MyBatis动态SQL中的主要元素,如下:
元素 | 说明 |
判断语句,用于单条件分支判断 | |
相当于Java中的switch..case...default | |
补助元素,用于处理一些SQL拼接,特殊字符问题 | |
循环语句,常用于in语句等例举条件中 | |
从OGNL表达式中创建一个变量,并将其绑定到上下文中,常用于模糊查询的sql中 |
二.
1.
在MyBatis中,
(1)在映射文件中使用
<select id="findCustomerByNameAndJobs" parameterType="com.itheima.po.Customer" resultType="com.itheima.po.Customer"> select * from t_customer where 1=1 <if test="username != null and username !='' "> and username like concat('%',#{username},'%') if> <if test="jobs != null and jobs !='' "> and jobs=#{jobs} if> select>
使用
(2)在测试类中,编写测试方法findCustomerByNameAndJobs:
/** * 根据用户名和用户的工作来查询用户 */ @Test public void findCustomerByNameAndJobsTest() { //通过工具类来生成SqlSession对象 SqlSession sqlSession=MyBatisUtils.getSession(); //创建Customer对象,封装需要组合查询的条件 Customer customer=new Customer(); customer.setJobs("teacher"); customer.setUsername("jack"); //执行SqlSession对象的查询方法,返回一个结果集 Listcustomers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameAndJobs", customer); //输出查询结果 for(Customer customer2:customers) { System.out.println(customer2); } //关闭sqlSession sqlSession.close(); }
(3)测试结果:
(4)如果将上面代码中的设值注释掉,如下,就会将所有的信息都输出来:
1 /** 2 * 根据用户名和用户的工作来查询用户 3 */ 4 @Test 5 public void findCustomerByNameAndJobsTest() { 6 //通过工具类来生成SqlSession对象 7 SqlSession sqlSession=MyBatisUtils.getSession(); 8 //创建Customer对象,封装需要组合查询的条件 9 Customer customer=new Customer(); 10 //customer.setJobs("teacher"); 11 //customer.setUsername("jack"); 12 13 //执行SqlSession对象的查询方法,返回一个结果集 14 Listcustomers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameAndJobs", customer); 15 //输出查询结果 16 for(Customer customer2:customers) { 17 System.out.println(customer2); 18 } 19 20 //关闭sqlSession 21 sqlSession.close(); 22 }
2.
在
当客户名不可空,则只根据客户名进行客户筛选,
当客户名为空,职业不为空时就根据客户职业进行客户筛选,
如果两者都为空,就查询出所有电话不为空的客户信息
在此种情况下,是不能使用
(1).在映射文件中配置如下
<select id="findCustomerByNameOrJobs" parameterType="com.itheima.po.Customer" resultType="com.itheima.po.Customer"> select * from t_customer where 1=1 <choose> <when test="username != null and username != '' "> and username like concat('%',#{username},'%') when> <when test="jobs != null and jobs !='' "> and jobs=#{jobs} when> <otherwise> and phone is not null otherwise> choose> select>
在上述代码中,使用了
(2)创建测试方法
/** * 根据用户名或者职业来查询用户信息 */ @Test public void findCustomerByNameOrJobs() { //通过工具类来生成SqlSession对象 SqlSession sqlSession=MyBatisUtils.getSession(); //创建Customer对象,封装需要组合查询的条件 Customer customer=new Customer(); customer.setJobs("teacher"); customer.setUsername("jack"); //执行SqlSession对象的查询方法,返回一个结果集 Listcustomers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameOrJobs", customer); //输出查询结果 for(Customer customer2:customers) { System.out.println(customer2); } //关闭sqlSession sqlSession.close(); }
(3)测试结果
(4)当把customer.setUsername("jack")注释掉后,结果为:
(5)如果把customer.setJobs(“teacher”)也注释掉后,结果为:
3.
在前面的小节中编写的SQL后面都添加了“where 1=1 "这个条件,那么为什么要这么写呢?是应为如果将后面的”where 1=1“去掉,那么MyBatis拼接出来的SQL语句将会出错
SELECT *from t_customer where and usename like concat( '%',#{username}, '%');
在上面的语句中,where后面跟的是and,这样将出错,而加入了where 1=1 后,既保证了where后面的条件成立,也保证了避免where后面跟着是and或者or.
那么在Mybatis中有什么办法是可以不加入”1=1“这个条件也可以呢?
MyBatis提供了
在上面的语句中,使用
4.
在Hibernate中,如果要更新一条数据,就需要发送所有的字段给持久化对象,然而在实际应用中,大多数情况下,是只跟新某一个字段或者几个字段,如果更新的每一条数据都要将所有的属性都更新一遍,那么其执行效率是非常的低,有没有办法使程序只更新需要更新的字段?
在MyBatis中,提供了
例如,在入门案例中,使用
update t_customer <if test="username != null and username !='' "> username=#{username}, if> <if test="jobs != null and jobs != ''"> jobs=#{jobs}, if> <if test ="phone != null and phone != ''"> phone=#{phone}, if> where id=#{id}
在上面的sql语句中,使用了
在测试类中编写如下代码:
/** *使用set 更新客户 */ @Test public void updateCustomer1Test() { //1.通过工具类来生成SqlSession对象 SqlSession sqlSession=MyBatisUtils.getSession(); //2.创建Customer对象,并向对象中添加数据 Customer customer=new Customer(); customer.setId(3); customer.setPhone("444444"); //3.执行sqlSession的更新方法,返回的是受影响的行数 int rows=sqlSession.update("com.itheima.mapper.CustomerMapper.updateCustomer", customer); //4.根据返回的结果来判断是否更新成功 if(rows>0) { System.out.println("更新成功!"); }else { System.out.println("更新失败!"); } //5.提交事务 sqlSession.commit(); //6.关闭事务 sqlSession.close(); }
测试结果如下:
注意:使用
5.
该元素是用于数组和集合循环遍历。
<select id="findCustomerByIds" parameterType="List" resultType="com.itheima.po.Customer"> select * from t_customer where id in <foreach item="id" index="index" collection="list" open="(" separator="," close=")"> #{id} foreach> select>
在上述代码中,使用了
item:配置的是循环中当前的元素。
index:配置的是当前元素在集合的位置下标。
collection:配置的list是传递过来的参数类型(首字母小写),她可以是一个array,list(或者是collection),Map集合的建,POJO包装类中数组或者集合类型的属性名。
open和close:配置的是以什么符号将这些集合元素包装起来。
separate:配置的是各个元素之间的间隔符。
在测试类中编写如下代码,验证
/** * 根据客户编号批量查询客户信息 */ @Test public void findCustomerByIdsTest() { //1.获取SqlSession对象 SqlSession sqlSession=MyBatisUtils.getSession(); //2.创建一个list集合,用来封装查询的id值 Listids=new ArrayList (); ids.add(2); ids.add(3); //3.执行SqlSession的查询方法,返回结果集 List customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByIds", ids); //4.输出查询的结果 for(Customer customer:customers) { System.out.println(customer); } //5.关闭sqlSession sqlSession.close(); }
测试结果如下:
注意:在使用
(1)如果在调用该sql语句时,传入的是一个参数,并且参数的类型是一个数组或者是list的时候,该collection属性值就是array和list(或者collection)
(2)如果传入的是多个参数的时候,就需要把它们封装成一个Map,单个参数也是可以这样封装的,这时候collection属性值就是Map的键
(3)如果传进来的是POJO的包装类的时候,collection属性值就是该包装类中需要进行遍历的数组或者集合的属性名。