2、Mybatis框架-02

Mybatis框架-02

动态SQL

什么是动态sql?

可根据传进来的值不同,动态的生成sql语句。调用的是同一条sql语句,从而达到sql语句的复用。

比如说我要修改密码,sql语句只能修改密码,其他的不能修改我的。当我需要修改其他的东西的时候,调用的还是这条sql语句。他会动态的根据你的需要给你生成对应的sql语句。

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="cn.zj.mybatis.mapper.UserMapper">
    <!-- 动态sql语句 -->
    <!-- 修改,可根据不传入不同的字段 -->
    <!-- 
    <trim />:有;两个子标签:where、set
    prefix: 
    prefixOverrides:
     -->
    <update id="updateByPrimaryKey" parameterType="cn.zj.mybatis.pojo.User">
        update user 
        <!-- 
                    动态部分:set标签、if标签
          prefix: 如果是修改就写set,查询就写where
          suffixOverrides:删除where前面多余的逗号        
         -->
        <trim prefix="set" suffixOverrides=",">
            <if test="name != null">name = #{name},</if>
            <if test="password != null">password = #{password},</if>
            <if test="age != null">age = #{age},</if>
        </trim>
        where id = #{id}
    </update>
    
    <!-- 查询模糊查询 -->
    <select id="selectListByCondition" resultType="cn.zj.mybatis.pojo.User">
        select * from user 
        <!-- 使用include使用sql代码片段  refid:就是sql片段的id-->
        <include refid="select_user_condition"/>
    </select>
    <!-- sql标签:sql代码片段 -->
    <sql id="select_user_condition">
      <trim prefix="where" prefixOverrides="OR|AND">
        <!--
            字符串拼接函数 : concat('%',#{name},'%')
          -->
            <if test="name != null">name like concat('%',#{name},'%')</if>
            <if test="age != null">and age = #{age}</if>
        </trim>
    </sql>
    <!-- 查询总记录数 -->
    <!-- Long类型一定要返回 -->
    <select id="selectCountByCondition" resultType="long">
        select count(*) from user
        <include refid="select_user_condition"/>
    </select>
    
    <!-- 批量删除 -->
    <!--
    collection:所要循环的集合或数组
    item:所要循环的项
    open:循环开始需要添加什么
    close:循环结束需要添加什么
    separator:每个字段之间用什么分割
    -->
    <delete id="deleteByIds">
        delete from user where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </delete>
    
    <!-- 批量插入 -->
    <insert id="insertList">
        insert into user (name,password,age) values
        <foreach  collection="users" item="user" separator=",">
            (#{user.name},#{user.password},#{user.age})
        </foreach>
    </insert>
  </mapper>

注解方式:标签不是原来的那套标签了

@UpdateProvider:type是你放在一个java类中完成,method是调用所对应的方法。

@UpdateProvider(type = UserSqlProvider.class,method = "update")
	int update(User user);
	
	@SelectProvider(type = UserSqlProvider.class, method = "selectListByCondition")
	List<User> selectListByCondition(User user);
	
	@DeleteProvider(type = UserSqlProvider.class, method = "deleteByIds")
	int deleteByIds(@Param("ids") Integer[] ids);
	
	@InsertProvider(type = UserSqlProvider.class, method = "insertByBatch")
	int insertByBatch(@Param("users") List<User> users);

需要创建一个SQL对象,或者是自己拼接sql语句,返回一个String

package cn.zj.mybatis.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.jdbc.SQL;

import cn.zj.mybatis.pojo.User;

public class UserSqlProvider {
	
	public String update(User user) {
		//SQL对象
		SQL sql = new SQL();
		sql.UPDATE("user");//相当于update user 
		//不需要加逗号
		if(user.getName() != null) {
			sql.SET("name = #{name}");//set会自动拼接后面的条件并且加上逗号
		}
		if(user.getPassword() != null) {
			sql.SET("password = #{password}");
		}
		if(user.getAge() != null) {
			sql.SET("age = #{age}");
		}
		sql.WHERE("id = #{id}");
		System.out.println(sql.toString());
		return sql.toString();
	}
	//多条件模糊查询
	public String selectListByCondition(User user) {
		SQL sql = new SQL();
		sql.SELECT("*");//相当于	select *
		sql.FROM("user");//相当于 from user 
		if(user.getName() != null) {
			sql.WHERE("name like concat('%',#{name},'%')");//相当于where ... 默认两个字段之间使用and
		}
		if(user.getPassword() != null) {
			sql.WHERE("password = #{password}");
		}
		if(user.getAge() != null) {
			sql.OR();//默认是and连接
			sql.WHERE("age = #{age}");
		}
		System.out.println(sql.toString());
		return sql.toString();
	}
	//批量删除
	public String deleteByIds(@Param("ids") Integer[] ids) {
		SQL sql = new SQL();
		sql.DELETE_FROM("user");//相当于 delete from user
		StringBuilder sb = new StringBuilder();
		//只能使用普通的循环
		for (int i = 0; i < ids.length; i++) {
			sb.append("#{ids["+i+"]},");
		}
		sb.deleteCharAt(sb.length()-1);//删除最后一个多余的逗号,是sb的长度-1
		sql.WHERE("id in ("+sb.toString()+")");//没有的条件可以自己拼接
		System.out.println(sql.toString());
		return sql.toString();
	}
	//批量插入
	public String insertByBatch(@Param("users") List<User> users) {
		//当sql对象的方法满足不了我们需求时,我们需要手动拼接
		StringBuilder sb = new StringBuilder();
		sb.append("insert into user(name,password,age) values ");
		for (int i = 0; i < users.size(); i++) {
			sb.append("(#{users["+i+"].name},#{users["+i+"].password},#{users["+i+"].age}),");
		}
		sb.deleteCharAt(sb.length()-1);//将最后一个逗号去掉
		System.out.println(sb.toString());
		return sb.toString();
	}
}

多对一

实体类

department

package cn.zj.mybatis.pojo;

import java.util.List;

public class Department {
	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Department(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public Department() {
		super();
		// TODO Auto-generated constructor stub
	}
	
}

employee

package cn.zj.mybatis.pojo;

public class Employee {
	private Integer id;
	private String name;
	//多对一
	private Department dept;
	
	public Department getDept() {
		return dept;
	}
	public void setDept(Department dept) {
		this.dept = dept;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", dept=" + dept + "]";
	}
	public Employee() {
		super();
		// TODO Auto-generated constructor stub
	}
	
}

N+1策略

public interface Many2OneMapper {
	Employee selectByPrimayKey(Integer id);
}
<?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.Many2OneMapper">
	<!-- 多对一的N+1策略 -->
	<!-- 需要手动映射 -->
	<select id="selectByPrimayKey" resultMap="emp_map">
		select * from employee where id = #{id}
	</select>
	<resultMap id="emp_map"  type="cn.zj.mybatis.pojo.Employee">
		<!-- 主键列映射 -->
		<id column="id" property="id" />
		<!-- 非主键列映射 -->
		<result column="name" property="name" />
		<!-- 多对一的N+1策略需要使用association标签 -->
		<!-- 
		association:用于多对一的
		property:对一的属性
		column:所关联的列
		select:+1的id
		 -->
		<association property="dept" column="dept_id"
			select="selectDeptByDeptId" />
	</resultMap>
	<select id="selectDeptByDeptId"
		resultType="cn.zj.mybatis.pojo.Department">
        select * from department where id = #{id}
	</select>
</mapper>

等值策略

public interface Many2OneMapper {
	Employee selectByPrimayKey(Integer id);
}

<?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.Many2OneMapper">
  <!-- 多对一的等值策略 -->
  <!-- 需要手动映射 -->
    <select id="selectByPrimayKey" resultMap="emp_map">
     select e.id e_id, e.name e_name, d.id d_id, d.name d_name from employee e join department d on e.dept_id = d.id where e.id = #{id}
    </select>
    <resultMap id="emp_map" type="cn.zj.mybatis.pojo.Employee">
        <!-- 主键列映射 -->
        <id column="e_id" property="id"/>
        <result column="e_name" property="name"/>
        <!-- 使用association来表现多对一关系 -->
        <!-- 
        property:所对应多一方的属性 private Department dept
        javaType:实体类对应的全限定名
         -->
        <association property="dept" javaType="cn.zj.mybatis.pojo.Department">
            <!-- 主键列映射 -->
            <id column="d_id" property="id"/>
            <!-- 非主键列映射 -->
            <result column="d_name" property="name"/>
        </association>
    </resultMap>
  </mapper>

一对多

实体类

department

package cn.zj.mybatis.pojo;

import java.util.List;

public class Department {
	private Integer id;
	private String name;
	
	private List<Employee> emps;
	
	public List<Employee> getEmps() {
		return emps;
	}
	public void setEmps(List<Employee> emps) {
		this.emps = emps;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public Department() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Department(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	@Override
	public String toString() {
		return "Department [id=" + id + ", name=" + name + ", emps=" + emps + "]";
	}	
}

employee

package cn.zj.mybatis.pojo;

public class Employee {
	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + "]";
	}
	public Employee(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public Employee() {
		super();
		// TODO Auto-generated constructor stub
	}
}

N+1策略

public interface One2ManyMapper {
	
	List<Department> selectByPrimayKey(Integer id);
}
<?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.One2ManyMapper">
	<!--一对多 N+1策略  -->
	<select id="selectByPrimayKey" resultMap="dept_map">
	   select * from department where id = #{id}
	</select>
	<resultMap id="dept_map" type="cn.zj.mybatis.pojo.Department">
	   <!-- 主键列映射 -->
	   <id column="id" property="id"/>
	   <!-- 非主键列映射 -->
	   <result column="name" property="name"/>
	   <!-- N+1 一对多使用collection标签-->
	   <!-- 
	   property:所要映射到的属性列,集合的属性名
	   column:与第二条sql语句关联的列的属性
	   select:+1所需要的id
	    -->
	   <collection property="emps" column="id" select="select_emp_dept_id"/>
	</resultMap>
	<!-- +1 -->
	<select id="select_emp_dept_id" resultType="cn.zj.mybatis.pojo.Employee">
	   select * from employee where dept_id = #{id}
	</select>
</mapper>

等值策略

public interface One2ManyMapper {
	
	List<Department> selectByPrimayKey(Integer id);
}
<?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.One2ManyMapper">
    <!-- 需要手动映射  -->
	<select id="selectByPrimayKey" resultMap="dept_map">
	   select d.id d_id, d.name d_name, e.id e_id, e.name e_name from department d join employee e on d.id = e.dept_id where d.id = #{id}
	</select>
	<resultMap id="dept_map" type="cn.zj.mybatis.pojo.Department">
	   <!-- 主键列映射 -->
	   <id column="d_id" property="id"/>
	   <!-- 非主键列 -->
	   <result column="d_name" property="name"/>
	   <!-- 一对多关系映射  -->
	   <!-- 
	       ofType:所要映射的实体类的全限定名
	    -->
	   <collection property="emps" ofType="cn.zj.mybatis.pojo.Employee">
	       <!-- 主键列映射 -->
	       <id column="e_id" property="id"/>
	       <!-- 非主键映射 -->
	       <result column="e_name" property="name"/>
	   </collection>
	</resultMap>
</mapper>

缓存

为什么需要缓存呢?

只有在持久层才有缓存说法。

当我们重复的去请求sql都会做IO操作。如果我们每次都从数据库中去取数据的话。会打打的影响我们的效率。

如果我们将重复的请求放在缓存那里,让第一次请求在数据库中去取,第二次,第三次都让其取缓存中取,这样就大大的提高了我们查询的效率。

缓存分为一级缓存和二级缓存。

一级缓存

一级缓存是mybatis自带的,内置在sqlSession对象中,默认是开启的。可以手动的设置关闭缓存。

session1.clearCache();关闭缓存

二级缓存

一级缓存是SqlSession对象级别,在每一次会话中有效

二级缓存是 SqlSessionFactory级别,在整个应用都有效,可以在多个会话有效

MyBatis本身并没有实现二级缓存

二级缓存需要第三方缓存提供商的支持

Ehcache -第三方缓存(Hibernate框架默认就是支持)

学习:http://www.mybatis.org/ehcache-cache/

下载Ehcache : https://github.com/mybatis/ehcache-cache/releases

mybatis默认开启二级缓存;模拟一千次sqlSession对象的请求

导入ehcache包

导入依赖的日志包

xml中映射配置二级缓存

<!-- 配置缓存 -->
	<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>

ehcache.xml配置(配置全局的二级缓存)

<ehcache>
	<!-- 缓存的磁盘位置 -->
	<diskStore path="D:/mybatis_cache"/>
	<!-- 默认的缓存策略: 如果开发者在某一个需要缓存的文件配置了自定义缓存,就不使用默认的,如果没有配置,就使用默认缓存策略-->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
      />
</ehcache>

因为二级缓存可以缓存到文件(将对象序列化到本地),涉及到对象序列化,那么对应的javaBean对象就必须实现 。

命中率= 从缓存中获取数据的次数/ 查询的总次数

如 : 两次查询 从缓中获取一次

0.5 = 1/2;

0.666666 = 2/3;

命中率越高缓存效果越好

DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - ==>  Preparing: select * from user where id = ? 
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.5
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.6666666666666666
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.75
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.8
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.8333333333333334
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.8571428571428571
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.875
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.8888888888888888
DEBUG [main] - Cache Hit Ratio [cn.zj.mybatis.mapper.UserMapper]: 0.9

逆向工程

就是可以根据数据库的字段动态的生成我们的实体类pojo,还有一些常用的sql语句。

首先需要安装mybatis插件在eclipse中。

然后new一个mybatis的generator,生成generatorConfig.xml配置文件。

generatorConfig.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>

    <context id="sqlGenerate" targetRuntime="MyBatis3">
        <!-- 是否去除自动生成的注释 true:是 : false:否 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!-- 数据库链接URL、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&amp;characterEncoding=utf-8&amp;"
            userId="root" password="root">
        </jdbcConnection>

         <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer;
         为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- 生成Pojo包名和位置 -->
        <javaModelGenerator targetPackage="cn.zj.mybatis.pojo"
            targetProject="generrator/src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="true" />
            <!-- 清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- 生成Mapper映射XML文件位置 -->
        <sqlMapGenerator targetPackage="cn.zj.mybatis.mapper"
            targetProject="generrator/src">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- 生成Mapper接口文件位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.zj.mybatis.mapper" targetProject="generrator/src">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!-- 要生成哪些表(更改tableName和domainObjectName就可以) -->
        <!-- tableName:要生成的表名
        domainObjectName:生成后的实例名
        enableCountByExample:Count语句中加入where条件查询,默认为true开启
        enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
        enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
        enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
        selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
        -->
       <!--  <table tableName="user"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="true"
            selectByExampleQueryId="false" /> -->
        <table tableName="department" 
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="true"
            selectByExampleQueryId="false" />
        <table tableName="employee"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="true"
            selectByExampleQueryId="false" />
    </context>
</generatorConfiguration>

逆向工程的使用

逆向工程没有生成插入方法的插入之后返回id值需手动添加。

@Test
	public void testSelectByExample() {
		// 1.创建sqlSession对象
		SqlSession session = MybatisUtils.openSqlSession();
		// 2.调用getMapper()得到代理对象
		DepartmentMapper mapper = session.getMapper(DepartmentMapper.class);
		DepartmentExample example = new DepartmentExample();
		Criteria criteria = example.createCriteria();//创建限制对象;
		//一个限制对象默认是用and连接两个字段,想要用or连接必须要创建两个限制对象。
		criteria.andIdGreaterThan(2);
		criteria.andNameLike("%部%");
		List<Department> list = mapper.selectByExample(example);
		for (Department department : list) {
			System.out.println(department);
		}
		// 5.关闭连接
		session.close();
	}
		@Test
	public void testInsert() {
		// 1.创建sqlSession对象
		SqlSession session = MybatisUtils.openSqlSession();
		// 2.调用getMapper()得到代理对象
		DepartmentMapper mapper = session.getMapper(DepartmentMapper.class);
		Department dept = new Department(null, "网络部");
		int row = mapper.insert(dept);
		System.out.println(row);
		System.out.println(dept.getId());
		// 4.提交事务
		session.commit();
		// 5.关闭连接
		session.close();
	}
	
posted @ 2022-04-02 13:14  站着说话不腰疼  阅读(52)  评论(0)    收藏  举报