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 @   站着说话不腰疼  阅读(43)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示