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&characterEncoding=utf-8&" 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(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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 重磅开源!