zoukankan      html  css  js  c++  java
  • MyBatis(三)动态SQL与缓存

    个人博客网:https://wushaopei.github.io/    (你想要这里多有)

    一、动态SQL语句

    准备工作:

    public class User {
    	private int id;
    	private String lastName;
    	private int sex;

    1、if 语句

    说明: if语句,可以动态的根据你的值来决定,是否需要动态的添加查询条件。

    方法代码:

    public interface UserMapper {
    	/**
    	 * 根据用户的lastName属性和sex属性查询用户信息<br/>
    	 * 	前提条件是lastName属性和sex属性值都合法。<br/>
    	 * 	如果lastName属性和sex属性哪个不合法,就不要加入查询条件
    	 */
    	public List<User> queryUserByNameAndSex(User user);
    }

    配置信息:

    	<select id="queryUserByNameAndSex" resultType="com.webcode.pojo.User">
    		select 
    			id,last_name lastName,sex 
    		from 
    			t_user 
    		where 
    		<!-- 做if判断 -->
    		<if test="lastName != null">
    			last_name like concat('%',#{lastName},'%') 
    		</if>
    		<if test="sex == 0 || sex == 1">
    			and sex = #{sex}
    		</if>
    	</select>
    

    测试代码:

            @Test
    	public void testQueryUserByNameAndSex() {
    		SqlSession session = sqlSessionFactory.openSession();
    		try {
    			UserMapper mapper = session.getMapper(UserMapper.class);
    			
    			mapper.queryUserByNameAndSex(new User(null, "bb", 10)).forEach(System.out::println);
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			session.close();
    		}
    	}
    

    2、where 语句

    说明: where语句,可以帮我们在多个动态语句中,有效的去掉前面的多余的and  或 or 之类的多余关键字

    	<select id="queryUserByNameAndSex" resultType="com.webcode.pojo.User">
    		select 
    			id,last_name lastName,sex 
    		from 
    			t_user 
    		<!-- where标签可以动态判断里面有没有内容。如果没有内容。就没有where关键字,有内容就有where关键字,并且可以去掉里面包含的多余的and或or关键字 -->
    		<where>
    			<!-- 做if判断 -->
    			<if test="lastName != null">
    				last_name like concat('%',#{lastName},'%') 
    			</if>
    			<if test="sex == 0 || sex == 1">
    				and sex = #{sex}
    			</if>
    		</where> 
    	</select>

    3、trim语句

    说明: trim 可以动态在包含的语句前面和后面添加内容。也可以去掉前面或者后面给定的内容

    • prefix 前面添加内容
    • suffix 后面添加内容
    • suffixOverrides 去掉的后面内容
    • prefixOverrides 去掉的前面内容

    方法代码:

    public List<User> queryUserByNameAndSexTrim(User user);

    配置信息:

    <select id="queryUserByNameAndSexTrim" resultType="com.webcode.pojo.User">
    		select 
    			id,last_name lastName,sex 
    		from 
    			t_user 
    		<!-- 
    			trim 语句可以去掉包含内容的前面或后台的指定内容
    				prefixOverrides="and" 	去掉前缀add
    				suffixOverrides="and"	去掉后缀add
    				prefix					在内容前面添加where
    				suffix=""				在内容后面添加
    		-->
    		<trim prefixOverrides="and" suffixOverrides="and" prefix="where" >
    			<!-- 做if判断 -->
    			<if test="lastName != null">
    				last_name like concat('%',#{lastName},'%') and 
    			</if>
    			<if test="sex == 0 || sex == 1">
    				sex = #{sex}
    			</if>
    		</trim> 
    	</select>

    测试代码:

    public void testQueryUserByNameAndSexTrim() {
    		SqlSession session = sqlSessionFactory.openSession();
    		try {
    			UserMapper mapper = session.getMapper(UserMapper.class);
    			
    			mapper.queryUserByNameAndSexTrim(new User(null, "bb", 10)).forEach(System.out::println);
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			session.close();
    		}
    	}

    4、choose( when , otherwise )语句

    说明:choose when otherwise 可以执行多路选择判断,但是只会有一个分支会被执行。

             类似switch case 语句

    方法:

    	/**
    	 * 根据user对象的属性进行查询<br/>
    	 * 	1、如果lastName值有效(非空),则做模糊查询<br/>
    	 *  2、sex属性如果有效,就做性别查询<br/>
    	 *  3、使用默认条件查询
    	 */
    	public List<User> queryUsersByNameOrSexChoose(User user);

    配置信息:

    <select id="queryUsersByNameOrSexChoose" resultType="com.webcode.pojo.User">
    		select
    			id,last_name lastName,sex 
    		from 
    			t_user
    		<where>
    			<choose>
    				<when test="lastName != null">
    					last_name like concat('%',#{lastName},'%')
    				</when>
    				<when test="sex == 1 || sex == 0">
    					sex = #{sex}
    				</when>
    				<otherwise>
    					1 = 1
    				</otherwise>
    			</choose>
    		</where>
    	</select>

    测试代码:

            @Test
    	public void testQueryUsersByNameOrSexChoose() {
    		SqlSession session = sqlSessionFactory.openSession();
    		try {
    			UserMapper mapper = session.getMapper(UserMapper.class);
    			
    			mapper.queryUsersByNameOrSexChoose(new User(null, null, 10)).forEach(System.out::println);
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			session.close();
    		}
    	}

    5、set语句

           删除条件后的逗号

    方法:

            /**
    	 * 更新user
    	 */
    	public int updateUser(User user);

    配置信息:

        <update id="updateUser" parameterType="com.webcode.pojo.User">
    		update 
    			t_user 
    		<!-- set可以删除条件后的逗号 -->
    		<set>
    			<if test="lastName != null">
    				last_name = #{lastName} , 
    			</if>
    			<if test="sex == 1 || sex == 0">
    				sex = #{sex}
    			</if>
    		</set>
    		where 
    			id = #{id} 
    	</update>

    测试代码:

            @Test
    	public void testUpdateUser() {
    		SqlSession session = sqlSessionFactory.openSession();
    		try {
    			UserMapper mapper = session.getMapper(UserMapper.class);
    			
    			mapper.updateUser(new User(4, "ccc", 10));
    			
    			session.commit();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			session.close();
    		}
    	}

    6、foreach语句

    方法:

            /**
    	 * select * from t_user where id in(1,2,3)
    	 */
    	public List<User> queryUsersByIds(List<Integer> ids);

    配置信息:

            <select id="queryUsersByIds" resultType="com.webcode.pojo.User">
    		select 
    			id,last_name lastName,sex
    		from 
    			t_user
    		where
    			id in 
    		<!-- 
    			foreach是做遍历操作
    				collection属性是遍历的集合
    				open是遍历之前要添加的内容
    				close是遍历之后要添加的内容
    				separator是每遍历的元素中间要加的内容
    				item 是当前正在遍历的内容
    				
    		 -->
    		<foreach collection="list" open="(" close=")" separator="," item="i">
    			#{i}
    		</foreach>
    	</select>
    

    测试代码:

            @Test
    	public void testQueryUsersByIds() throws Exception {
    		SqlSession session = sqlSessionFactory.openSession();
    		try {
    			UserMapper mapper = session.getMapper(UserMapper.class);
    
    			List<Integer> ids = new ArrayList<Integer>();
    			ids.add(1);
    			ids.add(2);
    			ids.add(3);
    			ids.add(4);
    			mapper.queryUsersByIds(ids).forEach(System.out::println);
    
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			session.close();
    		}
    	}

    二、mybatis缓存

    缓存:所谓是指把一些经常访问的数据保存到一个调整的缓冲区中。保存在高速缓冲区中的数据叫缓存。

    一级缓存:指的是缓存在SqlSession中的数据(默认开启,并且不能关闭)

    二级缓存:指的是缓存在SqlSessionFactory中的数据(需要手动开启和配置)

    1、mybatis的一级缓存的示例

    一级缓存测试代码:

            @Test
    	public void testFirstLevelCache() throws Exception {
    
    		SqlSession session = sqlSessionFactory.openSession();
    		
    		try {
    			
    			UserMapper mapper = session.getMapper(UserMapper.class);
    			
    			System.out.println( mapper.queryUserById(1) );
    			
    			System.out.println( mapper.queryUserById(1) );
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			session.close();
    		}
    	}

    1.2、一级缓存的管理

    一级缓存失效的四种情况:

         1.不在同一个SqlSession对象中

            @Test
    	public void testFirstLevelCacheFail1() throws Exception {
    		queryOne();
    		queryOne();
    	}
    	
    	private void queryOne(){
    		SqlSession session = sqlSessionFactory.openSession();
    		UserMapper mapper = session.getMapper(UserMapper.class);
    		System.out.println(mapper.queryUserById(1));
    		session.close();
    	}

          2.执行语句的参数不同。缓存中也不存在数据。

            @Test
    	public void testFirstLevelCacheFail2() throws Exception {
    		SqlSession session = sqlSessionFactory.openSession();
    		UserMapper mapper = session.getMapper(UserMapper.class);
    		System.out.println(mapper.queryUserById(1));
    		System.out.println(mapper.queryUserById(2));
    		session.close();
    	}

          3.执行增,删,改,语句,会清空掉缓存

            @Test
    	public void testFirstLevelCacheFail3() throws Exception {
    		SqlSession session = sqlSessionFactory.openSession();
    		UserMapper mapper = session.getMapper(UserMapper.class);
    		System.out.println(mapper.queryUserById(1));
    		
    		mapper.updateUser(new User(1, "cadsrq", 1));//执行增,删,改,语句,会清空掉缓存
    		
    		System.out.println(mapper.queryUserById(1));
    		session.close();
    	}

           4.手动清空缓存数据

            @Test
    	public void testFirstLevelCacheFail4() throws Exception {
    		SqlSession session = sqlSessionFactory.openSession();
    		UserMapper mapper = session.getMapper(UserMapper.class);
    		System.out.println(mapper.queryUserById(1));
    		session.clearCache();//清空缓存
    		System.out.println(mapper.queryUserById(1));
    		session.close();
    	}

    2、mybatis的二级缓存

    二级缓存的图解示意

    二级缓存的使用:

    myBatis的二级缓存默认是不开启的。

            1、我们需要在mybatis的核心配置文件中配置setting选项

    	<settings>
    		<!-- 开启二级缓存 -->
    		<setting name="cacheEnabled" value="true"/>
    	</settings>

            2、在Mapper的配置文件中加入cache标签。

            <!-- 表示使用二级缓存 -->
    	<cache></cache>

            3、需要被二级缓存的对象必须要实现java的序列化接口。

             

    2.1、二级缓存的演示

            private void queryOne(){
    		SqlSession session = sqlSessionFactory.openSession();
    		UserMapper mapper = session.getMapper(UserMapper.class);
    		System.out.println(mapper.queryUserById(1));
    		session.close();
    	}
    	@Test
    	public void testSecondLevelCache() throws Exception {
    		queryOne();
    		queryOne();
    	}

    2.2、useCache="false"的演示和说明 

    	<!-- 
     		userCache 设置是否使用二级缓存,默认是true,使用
     		false 表示不使用二级缓存
     	 -->
    	<select id="queryUserById" resultType="com.webcode.pojo.User" parameterType="int" useCache="true">
    		select id,last_name lastName,sex from t_user where id = #{id}
    	</select>

    2.3、flushCache="false"的演示和说明 

    flushCache刷新或清空缓存

            <!--  flushCache是设置是否要清空缓存
    			默认是true,表示清空
    			false表示不清空缓存
             -->
      	<update id="updateUser" parameterType="com.webcode.pojo.User" flushCache="false">
      		update t_user set last_name = #{lastName},sex = #{sex} where id = #{id}
      	</update>

    2.4、<cache></cache>标签的介绍和说明

    默认的<cache/>标签的作用:

    1、映射语句文件中的所有 select 语句将会被缓存。

    2、射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。

    3、缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。

    4、根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。

    5、缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。

    6、缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

    cache标签示例解析:

    <cache    eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>

    eviction 属性表示缓存策略。

    • LRU – 最近最少使用的:移除最长时间不被使用的对象(这是默认策略)
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

    flushInterval       属性表示间隔多长时间刷新一下缓冲区,清理一下溢出的数据。以毫秒为单位。

    size                    属性表示缓存中可以保存多少个对象。默认是1024。

    readOnly            属性表示是否只读。如果设置为true。表示缓存中只有一个对象。如果设置为false(默认为false)每次取出来都会反序列化拷贝一份。

    type                    属性表示自定义二级缓存对象。

    自定义二级缓存:

    1. 编写一个类去实现Cache接口
    2. 到mapper配置文件的cache标签中配置type属性
    <cache type="com.webcode.cache.MyCache"></cache>
    public class MyCache implements Cache {
    
    	private String id;
    
    	private Map<Object, Object> cache = new HashMap<Object, Object>();
    
    	public MyCache(String id) {
    		this.id = id;
    	}
    
    	@Override
    	public String getId() {
    		return id;
    	}
    
    	@Override
    	public int getSize() {
    		return cache.size();
    	}
    
    	@Override
    	public void putObject(Object key, Object value) {
    		System.out.println("保存缓存");
    		cache.put(key, value);
    	}
    
    	@Override
    	public Object getObject(Object key) {
    		System.out.println("获取缓存");
    		return cache.get(key);
    	}
    
    	@Override
    	public Object removeObject(Object key) {
    		return cache.remove(key);
    	}
    
    	@Override
    	public void clear() {
    		cache.clear();
    	}
    
    	@Override
    	public ReadWriteLock getReadWriteLock() {
    		return null;
    	}
    
    	@Override
    	public boolean equals(Object o) {
    		if (getId() == null) {
    			throw new CacheException("Cache instances require an ID.");
    		}
    		if (this == o) {
    			return true;
    		}
    		if (!(o instanceof Cache)) {
    			return false;
    		}
    
    		Cache otherCache = (Cache) o;
    		return getId().equals(otherCache.getId());
    	}
    
    	@Override
    	public int hashCode() {
    		if (getId() == null) {
    			throw new CacheException("Cache instances require an ID.");
    		}
    		return getId().hashCode();
    	}
    
    }

    3、缓存的使用顺序说明

    • 当我们执行一个查询语句的时候。mybatis会先去二级缓存中查询数据。如果二级缓存中没有。就到一级缓存中查找。
    • 如果二级缓存和一级缓存都没有。就发sql语句到数据库中去查询。
    • 查询出来之后马上把数据保存到一级缓存中。
    • 当SqlSession关闭的时候,会把一级缓存中的数据保存到二级缓存中。

    org.apache.ibatis.cache.impl.PerpetualCache一级缓存实现类

    三、mybatis逆向工程

    MyBatis逆向工程,简称MBG。是一个专门为MyBatis框架使用者定制的代码生成器。可以快速的根据数据库表生成对应的映射文件,接口,以及Bean类对象。

    在Mybatis中,有一个可以自动对单表生成的增,删,改,查代码的插件。

    叫 mybatis-generator-core-1.3.2。

    它可以帮我们对比数据库表之后,生成大量的这个基础代码。

    这些基础代码有:

    1. 数据库表对应的javaBean对象
    2. 这些javaBean对象对应的Mapper接口
    3. 这些Mapper接口对应的配置文件
      	<!-- 去掉全部的注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

    1、准备数据库表

    create database mbg;
    
    use mbg;
    
    create table t_user(
    	`id` int primary key auto_increment,
    	`username` varchar(30) not null unique,
    	`password` varchar(40) not null,
    	`email` varchar(50)
    );
    
    insert into t_user(`username`,`password`,`email`) values('admin','admin','admin@atguigu.com');
    insert into t_user(`username`,`password`,`email`) values('wzg168','123456','admin@atguigu.com');
    insert into t_user(`username`,`password`,`email`) values('admin168','123456','admin@atguigu.com');
    insert into t_user(`username`,`password`,`email`) values('lisi','123456','admin@atguigu.com');
    insert into t_user(`username`,`password`,`email`) values('wangwu','123456','admin@atguigu.com');
    
    create table t_book(
    	`id` int primary key auto_increment,
    	`name` varchar(50),
    	`author` varchar(50),
    	`price`	decimal(11,2),
    	`sales`	int,
    	`stock` int
    );
    
    ## 插入初始化测试数据
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , 'java从入门到放弃' , '国哥' , 80 , 9999 , 9);
    
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13);
    
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52);
    
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , '木虚肉盖饭' , '小胖' , 16, 1000 , 50);
    
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95);
    
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , '蛋炒饭' , '周星星' , 9.9, 12 , 53);
     
    insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` ) 
    values(null , '赌神' , '龙伍' , 66.5, 125 , 535);
    
    select * from t_user;
    select * from t_book;

    mbg配置文件内容:

    <?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>
      <!-- 
      		targetRuntime="MyBatis3" 生成豪华版 CRUD(多了QBC查询)
      		targetRuntime="MyBatis3Simple"		生成标准版的CRUD
       -->
      <context id="DB2Tables" targetRuntime="MyBatis3Simple">
      	<!-- 去掉全部的注释 -->
    	<commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
      	<!-- 
      		jdbcConnection 配置数据库的四个连接属性
      	 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mbg"
            userId="root"
            password="root">
        </jdbcConnection>
    
        <javaTypeResolver >
          <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
    
    	<!-- 
    		javaModelGenerator 配置javaBean的生成信息
    			targetPackage="com.webcode.pojo" 配置生成的javaBean的包名
    			targetProject=".src"	表示输出到当前工程的src目录下
    	 -->
        <javaModelGenerator targetPackage="com.webcode.pojo" targetProject=".src">
          <property name="enableSubPackages" value="true" />
          <property name="trimStrings" value="true" />
        </javaModelGenerator>
    	
    	<!-- 
    		sqlMapGenerator 配置生成Mapper配置文件 
    			targetPackage="com.webcode.mapper" 	生成的mapper接口在哪个包下
    			targetProject=".src"	生成的代码放在哪个位置(当前工程的src目录下)
    	 -->
        <sqlMapGenerator targetPackage="com.webcode.mapper"  targetProject=".src">
          <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
    
    	<!-- 
    		javaClientGenerator生成Mapper接口
    			targetPackage="com.webcode.mapper" 	生成的Mapper接口在哪个包下
    			targetProject=".src"		生成的Mapper接口输出在哪个位置(当前工程的src目录下)
    	 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.webcode.mapper"  targetProject=".src">
          <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
    
    	<!-- 
    		一张表对应一个table标签
    			tableName	表名
    	 -->
        <table tableName="t_user" domainObjectName="User" ></table>
        <table tableName="t_book" domainObjectName="Book" ></table>
    
      </context>
    </generatorConfiguration>
    

    运行mbg的代码

    import java.io.File;
    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.internal.DefaultShellCallback;
    
    public class Runner {
    
    	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);
    	}
    
    }

    文件路径:

  • 相关阅读:
    java:合并两个排序的链表(递归+非递归)
    BitSet: 有1千万个随机数,随机数的范围在1到1亿之间。现在要求写出一种算法,将1到1亿之间没有在随机数中的数求出来?
    【Java异常】Caused by: java.lang.IllegalArgumentException: method GET must not have a request body
    Java 语言规范要求 equals 方法具有的特性
    使用Java编写一个日历格式的方法
    【Java异常】The dependencies of some of the beans in the application context form a cycle
    Mysql字符串截取总结:left()函数、right()函数、substring()函数、substring_index()函数
    【Java异常】java.sql.SQLExcetion:Cannot convert value “0000-00-00 00:00:00” from column 9 to TIMESTAMP
    怎样将Beyond Compare添加到系统右键菜单
    【SVN异常】关于TortoiseSVNin目录下没有svn.exe执行程序文件的解决方案
  • 原文地址:https://www.cnblogs.com/wushaopei/p/11759233.html
Copyright © 2011-2022 走看看