动态SQL语句
动态SQL是什么:
就是相对与固定SQL。就是通过传入的参数不一样,可以组成不同结构的SQL语句. 这种根据参数的条件修改SQL结构的SQL语句,我们称为动态SQL语句.
动态SQL有什么用
1.根据条件组装不同结构的SQL语句,可以提高SQL代码的重用性.
2.满足某些特定需求,如,条件判断查询
基于XML的实现
标签包括
<sql> 用于声明公有的SQL语句块.,在操作标签中使用<include>调用 [不建议用]
不建议的原因,会导致代码难以维护。
<if> 类似java if(){},用于判断
<foreach>:类似java的foreach循环,一般用户批量处理的SQL语句
<trim> :切割标签,主要用于切割关键字的头和尾的字符.新版的Mybatis使用的几率很少.
<set>:使用 set标签就是SQL语言的set关键字,可以在update 的时候set 关键字后面的,逗号可以自动忽略
<where>:使用where标签作为SQL语言的where关键字,好处如果where后面的条件都不成立,忽略where关键字.
<choose> <when> <otherwise> : java的swithc case
<?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="cn.zj.mybatis.mapper.UserMapper"> <!-- 抽取一个sql片段 <sql id="sql片段的唯一标识"></sql> --> <sql id="condition_sql"> <!-- 动态SQL语句之 where 标签 ,内部具体条件 --> <where> <!-- 条件 <if test="">标签体内容</test> test :内部是一个布尔类型的结果, 如果为true 标签体中内容生效,如果为false,标签内容无效 concat ():串联起来括号内 --> <if test="username !=null">username like concat('%',#{username},'%') </if> <if test="age !=null">and age = #{age}</if> </where> </sql> <!-- 根据条件查询 --> <select id="selectList" parameterType="user" resultType="user"> <!-- 静态sql语句:语义固定 --> <!-- select * from user where name like concat('%',#{name},'%') and age = #{age} --> select * from user <!-- 引入sql片段 <include refid="condition_sql"/> refid:对应sql片段的id --> <include refid="condition_sql" /> </select> <select id="selectTotalByCondition" parameterType="user" resultType="long"> select count(*) from user <include refid="condition_sql" /> </select> </mapper>
package cn.zj.mybatis.test; import static org.junit.Assert.*; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import cn.zj.mybatis.mapper.UserMapper; import cn.zj.mybatis.pojo.User; import cn.zj.mybatis.util.MyBatisUtil; public class UserMapperTest { @Test public void testName() throws Exception { SqlSession openSession = MyBatisUtil.openSession(); UserMapper mapper = openSession.getMapper(UserMapper.class); User user=new User(); //user.setUsername("e"); user.setAge(20); List<User> selectList = mapper.selectList(user); long selectTotalByCondition = mapper.selectTotalByCondition(user); System.out.println("總數"+selectTotalByCondition); openSession.close(); }}
<?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="cn.zj.mybatis.mapper.UserMapper"> <!-- 修改用户信息 --> <update id="updateUserInfo" parameterType="user"> <!-- 静态sql语句:固定语义 --> <!-- update user set name = #{name},password=#{password},age = #{age} where id = #{id} --> update user <!-- 动态sql语句 set标签 --> <set> <if test="username !=null">username = #{username},</if> <if test="password !=null">password =#{password},</if> <if test="age !=null">age =#{age}</if> </set> where id =#{id} </update> </mapper>
@Test public void testUpdata() throws Exception { SqlSession openSession = MyBatisUtil.openSession(); UserMapper mapper = openSession.getMapper(UserMapper.class); User user = new User(); user.setId(5); user.setPassword("小明"); int updateUserInfo = mapper.updateUserInfo(user); openSession.commit(); openSession.close(); }
package cn.zj.mybatis.test; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import cn.zj.mybatis.mapper.UserMapper; import cn.zj.mybatis.pojo.User; import cn.zj.mybatis.util.MyBatisUtil; public class UserMapperTest { @Test public void testInsertUserInfo() { // 1.创建SqlSession会话对象 SqlSession session = MyBatisUtil.openSession(); // 2.创建UserMapper接口的代理对象(Java的动态代理) UserMapper mapper = session.getMapper(UserMapper.class); User user1 = new User(null, "xireejhef", "wffew", 10); User user2 = new User(null, "xijfdhxhef", "wffew", 10); User user3 = new User(null, "xijdfhhhef", "wffew", 10); List<User> user=Arrays.asList(user1,user2,user3); // 3.执行插入方法 int row = mapper.insertUserInfo(user); // 4.提交事务(Mybatis默认DML操作需要手动提交事务) System.out.println(row); session.commit(); // 5.关闭session session.close(); }
<insert id="insertUserInfo" parameterType="user"> <!-- insert into user (name,password,age)values('张三','zs',18),('李四','ls',20) --> <!-- ('张三','zs',18),('李四','ls',20) --> insert into user (username,password,age)values <foreach collection="users" item="user" separator=","> (#{user.username},#{user.password},#{user.age}) </foreach> </insert>
@Test public void testDelet() { SqlSession openSession = MyBatisUtil.openSession(); UserMapper userMapper = openSession.getMapper(UserMapper.class); Integer[] id= {4,5}; int user = userMapper.deleteByPrimaryKey(id); System.out.println(user); openSession.commit(); openSession.close(); }
<!-- 批量删除 --> <delete id="deleteByPrimaryKey" parameterType="int"> delete from user where id in <foreach collection="ide" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>
@Test public void testSelectList() throws Exception { SqlSession openSession = MyBatisUtil.openSession(); UserMapper mapper = openSession.getMapper(UserMapper.class); Integer[] id= {1,3}; List<User> selectList = mapper.selectList(id); for (User user : selectList) { System.out.println(user); } openSession.close(); } }
<!-- 批量查询 --> <select id="selectList" parameterType="int" resultType="user"> <!-- sql :select * from user where id in(1,2,3,4) 问题: 要动态拼接的sql : (1,2,3,4) 答: 使用 foreach 标签 <foreach collection="" item="" open="" close="" separator="">标签体</foreach> collection:需要循环的数组或者集合 item :每次循环出的数据赋值的变量,在标签体中使用 open : 循环开始的 圆括号 ( close :循环结束的圆括号) separator :每次循环的分隔符 逗号 《 --> select * from user where id in <foreach collection="ids" item="id" open="(" close=")" separator=","> #{id} </foreach> </select>
基于注解方式实现
动态sql除了支持xml方式以外,还是支持使用纯注解的方式
主要一下四个注解+对应动态sql语句的类文件
- @SelectProvider 动态查询SQL语句对应注解
- @InsertProvider 动态插入SQL语句对应注解
- @UpdateProvider 动态修改SQL语句对应注解
- @DeleteProvider 动态删除SQL语句对应注解
package cn.zj.mybatis.pojo; /* * 此类是专门编写 User表对应的动态SQL语句的类 */ public class UserProvide { /* * 拼接动态sql语句的方法 注意:内部的 参数使用还是使用 OGNL 表达式 */ public String selectByCondition(User user) { StringBuilder str = new StringBuilder(); str.append("select * from user WHERE 1 = 1"); // 拼接动态sql if (user.getUsername() != null) { str.append(" and username like concat('%',#{username},'%')"); } if (user.getAge() != null) { str.append(" and age = #{age}"); } return str.toString(); } public String selectTotalByCondition(User user) { StringBuilder str = new StringBuilder(); str.append("select count(*) from user WHERE 1 = 1"); // 拼接动态sql if (user.getUsername() != null) { str.append(" and username like concat('%',#{username},'%')"); } if (user.getAge() != null) { str.append(" and age = #{age}"); } return str.toString(); } }
package cn.zj.mybatis.test;
/** * 单行查询 * @param id 主键id * @return 查询结果封装的User对象 */ /* * 查询动态sql语句的注解 * @SelectProvider(type=cn.zj.mybatis.pojo.UserProvider.class,method="") * 属性 * type :拼接动态sql语句的类 * method :返回具体动态sql语句的方法名称 * 注意 :方法的参数类型个数,必须和功能方法的参数类型个数一模一样(规则) * 建议:返回动态sql语句方法的名称和 当前功能的方法名称一样 */ @SelectProvider(type=UserProvide.class,method="selectByCondition") List<User> selectTotalByCondition(User user); /** * 根据提交查询总数 * @param user * @return */ @SelectProvider(type=UserProvide.class,method="selectTotalByCondition") Long selectCondition(User user);
import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import cn.zj.mybatis.mapper.UserMapper; import cn.zj.mybatis.pojo.User; import cn.zj.mybatis.util.MyBatisUtil; public class UserMapperTest { @Test public void testSelectList() throws Exception { //查詢 SqlSession openSession = MyBatisUtil.openSession(); UserMapper mapper = openSession.getMapper(UserMapper.class); User users = new User(); users.setAge(10); users.setUsername("1"); mapper.selectTotalByCondition(users); mapper.selectCondition(users); openSession.close(); } }
package cn.zj.mybatis.pojo; /* * 此类是专门编写 User表对应的动态SQL语句的类 */ public class UserProvide { public String updateUserInfoa(User user) { StringBuilder str = new StringBuilder(); str.append("update user set "); if (user.getAge() !=null) { str.append("age =#{age},"); } if (user.getUsername() !=null) { str.append("username =#{username},"); } if (user.getPassword() !=null) { str.append("password =#{password},"); } // 删除最后一个逗号 str.deleteCharAt(str.length()-1); str.append(" where id =#{id}"); return str.toString(); } }
@UpdateProvider(type=UserProvide.class,method="updateUserInfoa") int updateUserInfo(User user);
@Test public void testupdate() { //修改 SqlSession openSession = MyBatisUtil.openSession(); UserMapper userMapper = openSession.getMapper(UserMapper.class); User user = new User(); user.setId(6); user.setUsername("mmmmmmmm"); user.setAge(100); user.setPassword("abcabc"); int user1 = userMapper.updateUserInfo(user); System.out.println(user1); openSession.commit(); openSession.close(); }
package cn.zj.mybatis.pojo; import org.apache.ibatis.annotations.Param; /* * 此类是专门编写 User表对应的动态SQL语句的类 */ public class UserProvide { public String deleteByPrimaryKeya(@Param("ide")Integer[] ide) { StringBuilder sb = new StringBuilder(); sb.append("delete from user where id in ("); for (int i = 0; i < ide.length; i++) { sb.append("#{ide["+i+"]},"); } sb.deleteCharAt(sb.length()-1); sb.append(")"); return sb.toString(); } }
@DeleteProvider(type=UserProvide.class,method="deleteByPrimaryKeya") int deleteByPrimaryKey(@Param("ide")Integer[] ide);
@Test public void testDelet() { //删除 SqlSession openSession = MyBatisUtil.openSession(); UserMapper userMapper = openSession.getMapper(UserMapper.class); Integer[] id= {6,7}; int user = userMapper.deleteByPrimaryKey(id); System.out.println(user); openSession.commit(); openSession.close(); }
package cn.zj.mybatis.pojo; import java.util.List; import org.apache.ibatis.annotations.Param; /* * 此类是专门编写 User表对应的动态SQL语句的类 */ public class UserProvide { public String insertUserInfo(@Param("users")List<User> users) { StringBuilder sb = new StringBuilder(); sb.append("insert into user(Username,password,age) values "); for (int i = 0; i <users.size(); i++) { sb.append("(#{users[" + i + "].username},#{users[" + i + "].password},#{users[" + i + "].age}),"); } sb.deleteCharAt(sb.length()-1); return sb.toString(); } }
/** * 批量插入 * @param users * @return */ @InsertProvider(type=UserProvide.class,method="insertUserInfo") int insertUserInfo(@Param("users")List<User> users);
@Test public void testInsertUserInfo() { // 1.创建SqlSession会话对象 SqlSession session = MyBatisUtil.openSession(); // 2.创建UserMapper接口的代理对象(Java的动态代理) UserMapper mapper = session.getMapper(UserMapper.class); User user1 = new User(null, "xireejhef", "wffew", 10); User user2 = new User(null, "xijfdhxhef", "wffew", 10); User user3 = new User(null, "xijdfhhhef", "wffew", 10); List<User> user=Arrays.asList(user1,user2,user3); // 3.执行插入方法 int row = mapper.insertUserInfo(user); // 4.提交事务(Mybatis默认DML操作需要手动提交事务) System.out.println(row); session.commit(); // 5.关闭session session.close();
缓存
在Mybatis里面,所谓的缓存就是将已经查询过的记录放在内存的缓冲区或文件上,这样如果再次查询,可以通过配置的策略,命中已经查询过的记录.从而提高查询的效率.
缓存作用
提高查询的效率.
一级缓存
Mybatis的缓存分为一级缓存 二级缓存
一级缓存:所谓的一级缓存就是会话(SqlSesion对象)级别的缓存,就是同一个会话,如果已经查询过的数据会保存一份在内存中,如果会话没有关闭,再次调用同样的方法查询,不会再查询数据库,而是直接从缓存中取出之前查询的数据.
一级缓存默认是打开的,而且是关闭不了的.
如何清空一级缓存.
1.关闭会话.close()
2.进行了操作(增删改),提交了commit();
3.手工清除缓存clearCache()
二级缓存
一级缓存是SqlSession对象级别,在每一次会话中有效
二级缓存是 SqlSessionFactory级别,在整个应用都有效,可以在多个会话有效
MyBatis本身并没有实现二级缓存
二级缓存需要第三方缓存提供商的支持
Ehcache -第三方缓存(Hibernate框架默认就是支持)
学习地址
http://www.mybatis.org/ehcache-cache/
下载ehcache
https://github.com/mybatis/ehcache-cache/releases
配置开启二级缓存
MyBatis开启二级缓存新版本已经默认支持开启二级缓存.可以不改
<settings> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true"/> </settings> |
1.导包
2.配置文件
缓存的xml
<ehcache> <!-- 缓存的磁盘位置 --> <diskStore path="D:/mybatis_cache"/> <!-- 默认的缓存策略: 如果开发者在某一个需要缓存的文件配置了自定义缓存,就不使用默认的,如果没有配置,就使用默认缓存策略--> <!-- maxElementsInMemory="10000" 内存最大缓存的对象个数 eternal="false" 是否是永久有效 timeToIdleSeconds="120" 最大缓存时长 timeToLiveSeconds="120" 最大有效时长 overflowToDisk="true" 超出内存个数以后是否缓存到磁盘 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> </ehcache>
缓存日志的properties文件
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.cn.zj.mybatis.mapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
在映射文件中配置<cache>以及配置对应的缓存策略
<mapper namespace="cn.zj.mybatis.dao.UserMapper"> <!-- 当前表的映射开启支持二级缓存,并设置相关的缓存提供商,以及缓存的相关配置 --> <cache type="org.mybatis.caches.ehcache.EhcacheCache" > <!--最大的空闲时间 --> <property name="timeToIdleSeconds" value="10000"/> <!-- 最大的在线时间 --> <property name="timeToLiveSeconds" value="20000"/> <!-- 内存的大小 b字节 m1 =1024k 1k=1024b --> <property name="maxEntriesLocalHeap" value="2000000"/> <!-- 文件的大小 b字节--> <property name="maxEntriesLocalDisk" value="20000000"/> <!-- 算法 LRU:最少使用优先, "LFU" or "FIFO:先进先出 --> <property name="memoryStoreEvictionPolicy" value="LRU"/> </cache> <select id="selectAll" resultType="User"> select * from user </select> </mapper>
在对象类中序列化
因为二级缓存可以缓存到文件(将对象序列化到本地),涉及到对象序列化,那么对应的javaBean对象就必须实现
缓存的命中率
命中率= 从缓存中获取数据的次数/ 查询的总次数
如 : 两次查询 从缓中获取一次
0.5 = 1/2;
0.666666 = 2/3;
命中率越高缓存效果越好
MyBatis的对象关系映射(难点重点)
在实际开发中,一个业务可能涉及到多个数据表的查询,那么多表查询就涉及连接查询(等值连接), 等值连接 表与表之间有一个外键关键
但是程序中最终获取的表封装的对象, 对象与对象之间是没有外键关系的,对象和对象之间只有依赖关系
对象之间关系主要是四种
一对一 关系
一个人对应身份证id,一个QQ号对应一个QQ空间
一对多 关系
一个部门对应多个员工
多对一 关系
多个员工对应一个部门
多对多 关系
多个学生对应多个老师,多个学生对应多个课程
什么关系应该从哪个对象作为中心点来看
一对多, 以one方作为中心点
MyBatis框架支持多表查询封装对象之间关系
<collection> 一对多查询
<association>多对一和一对一查询
多对1,以age为外键
一对多
<?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="cn.zj.mybatis.mapper.UserMapper"> <select id="testSelect" resultMap="use"> select * from user2 where age =#{age} </select> <!-- 手动映射 --> <resultMap type="cn.zj.mybatis.pojo.User2" id="use"> <result column="name" property="name"/> <!-- 问题: private User user; 部门对象如何映射? 解决方案:使用 联合查询标签 <collection property="" column="" select=""/>
property :需要映射的pojo对象的属性此时就是 dept column :已知对应dept对象的外键列 select :需要去查询的功能id --> <collection property="user" column="age" select="findByDeptId"/>
</resultMap> <!-- 联合查询的功能 --> <!-- 根据部门的id查询出对应的部门对象--> <select id="findByDeptId" parameterType="Integer" resultType="cn.zj.mybatis.pojo.User"> select * from user where age = #{age} </select> </mapper>
结果:
等值连接查询(1对多,其他和上面一样)
<?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="cn.zj.mybatis.mapper.UserMapper"> <select id="testSelect" resultMap="use"> select u.id u_id ,u.password u_password,u.username u_username,s.age s_age,s.name s_name from user u,user2 s where u.age =s.age and s.age=#{age} </select> <!-- 手动映射 --> <resultMap type="cn.zj.mybatis.pojo.User2" id="use"> <id column="s_age" property="age"/> <result column="s_name" property="name"/> <!-- 问题: private List<User> user; 员工集合如何映射? 解决方案:使用 <collection>集合标签,在内部映射多方(员工)的信息 <collection property="" column="" select=""/> property :需要映射的属性 此时就是 user ofType:需要映射集合user 的泛型 --> <collection property="user" ofType="cn.zj.mybatis.pojo.User"> <id column="u_id" property="id"/> <result column="u_username" property="username"/> <result column="u_password" property="password"/> <result column="u_age" property="age"/> </collection> </resultMap> </mapper>
MyBatis的逆向工程
MyBatis的逆向工程能自动帮开发者生成数据库表对应的 pojo实体文件,自动生成映射文件 自定生成表的各种(CRUD)的sql语句, 但是只能做单表操作,联合查询还得开发者自己动 使用逆向工程得先在Eclipse安装逆向工程的插件 |
1.安装插件
2.新建一个普通java项目,导入mybatis.jar包和数据库驱动包
3.