MyBatis
Mybatis
什么是mybatis?
- mybatis是一款优秀的持久化框架
- 历史:mybatis是apache的一个开源项目ibatis,2010年apache将他迁移到goodle code并改名为Mybatis,2013年迁移到github
为什么学习mybatis?
- 帮助我们将数据存到数据库中
- 传统的jdbc代码复杂,mybatis简化
优点
- 实现了sql和代码分开,提高了代码的可维护性。
- 提供了映射标签,支持对象关系映射
- 提供xml文件,支持动态sql
- 使用的人多
第一个mybatis程序
使用步骤
- 导入依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
- 配置mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
//配置数据库的驱动
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/clt?useUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="7536951"/>
</dataSource>
</environment>
</environments>
//绑定实现接口的xml文件
<mappers>
<mapper resource="com/chen/dao/daotestmapper.xml"/>
</mappers>
</configuration>
- 用xml文件实现dao的接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chen.dao.daotest">
//namespace绑定需要实现的mapper接口
<select id="blogList" resultType="com.chen.pojo.Blog">
select * from blog
</select>
</mapper>
- 构建工具类
package com.chen;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class TestUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用mybatis提供的Resources工具类来将配置文件转化为流
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//使用sqlsessionfactorybuilder构建sqlsessionfactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//salsessionfactory开启sqlsession
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
- 开启测试
import com.chen.TestUtil;
import com.chen.dao.daotest;
import com.chen.pojo.Blog;
import com.mysql.cj.jdbc.Blob;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class Test {
public static void main(String[] args) {
SqlSession session = TestUtil.getSession();
//通过dao接口的class得到实现类
daotest mapper = session.getMapper(daotest.class);
List<Blog> blogs = mapper.blogList();
for (Blog blob : blogs) {
System.out.println(blob);
}
}
}
- 测试结果
Blog{id='0e5f55c5780345589da884efe43d952b', title='HelloSpring', author='陈浪涛01', time=Fri Jul 15 21:23:13 CST 2022, price=100}
Blog{id='1', title='淘气包马小跳', author='陈浪涛', time=Tue Jul 19 18:30:07 CST 2022, price=700}
Blog{id='252223ce1ae04dea82bb551beb6256a8', title='HelloJava', author='陈浪涛大帅比', time=Fri Jul 15 21:23:13 CST 2022, price=100}
Blog{id='2ec3228051fa4fddb231a4f5a875c643', title='HelloSpringMvc', author='陈浪涛02', time=Sat Jul 16 12:21:34 CST 2022, price=100}
Blog{id='5f1efc7a9ace496d864917e4f66d2874', title='HelloSpring', author='陈浪涛01', time=Sat Jul 16 12:21:34 CST 2022, price=100}
Blog{id='67a76c202ef64cc0948a6ba40b197a0c', title='HelloMybits', author='陈浪涛', time=Sat Jul 16 12:21:34 CST 2022, price=100}
Blog{id='6def317a0a734db98bd8b043208f5858', title='HelloMybits', author='陈浪涛03', time=Fri Jul 15 21:23:12 CST 2022, price=100}
Blog{id='c8e60711210c46a1b8011fa910f82f3f', title='HelloJava', author='陈浪涛', time=Sat Jul 16 12:21:34 CST 2022, price=100}
Blog{id='da8068c38f9b4cc4b3169fa686a23081', title='HelloSpringMvc', author='陈浪涛02', time=Fri Jul 15 21:23:13 CST 2022, price=100}
Blog{id='rfafdsg', title='淘气包马小跳', author='陈浪涛', time=Thu Jul 21 08:26:00 CST 2022, price=700}
Blog{id='rfgadffahar', title='淘气包马小跳', author='陈浪涛', time=Thu Jul 21 08:45:46 CST 2022, price=700}
Blog{id='rfgadffahardf', title='淘气包马小跳', author='陈浪涛', time=Thu Jul 21 08:51:31 CST 2022, price=700}
Blog{id='rr', title='淘气包马小跳', author='陈浪涛', time=Tue Jul 19 18:39:17 CST 2022, price=700}
Blog{id='rrr', title='淘气包马小跳', author='陈浪涛', time=Tue Jul 19 18:31:38 CST 2022, price=700}
Blog{id='rrrrrr', title='淘气包马小跳', author='陈浪涛', time=Tue Jul 19 18:40:02 CST 2022, price=700}
Blog{id='rrrrrrew', title='淘气包马小跳', author='陈浪涛', time=Wed Jul 20 09:46:18 CST 2022, price=700}
Blog{id='rrrrrrewerr', title='淘气包马小跳', author='陈浪涛', time=Wed Jul 20 09:47:26 CST 2022, price=700}
mybatis实现CRUD
daomapper
//查询
List<Blog> blogList();
//增加
int addBlog(Blog blog);
//删除
int deleteBlog(String id);
//修改
int updateBlog(Map<String,Object> map);
mapperxml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chen.dao.daotest">
<!-- 查询-->
<select id="blogList" resultType="com.chen.pojo.Blog">
select * from blog
</select>
<!-- 增加-->
<insert id="addBlog" parameterType="com.chen.pojo.Blog">
insert into blog values(#{id},#{title},#{author},#{time},#{price})
</insert>
<!-- 删除-->
<delete id="deleteBlog" parameterType="string">
delete from blog where id = #{id}
</delete>
<!-- 修改-->
<update id="updateBlog" parameterType="map">
update blog set author = #{author} where id = #{id}
</update>
</mapper>
resultType和parameterType简介
- resultType是sql返回的结果类型,如果没有配置别名,直接写实体类的全名如resultType="com.chen.pojo.Blog"
- parameterType是参数类型,写数据类型对应的别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
参数如何和sql中的对应
- mapper接口中参数的名字和sql中的一样,且mapper中要配置paramtertype
- 在mapper接口中使用注解@Param("sql中的参数名")来绑定 如:int deleteBlog(@Param(value = "id") String id1);,这种就不用在mapperxml中配置paramtertype
如果有多个基本数据类型怎么对应(不用pojo,使用Map)
-
原因是sql中的参数名会和map中的key值对应,取value作为参数
-
mapper接口
int updateBlog(Map<String,Object> map);
- mapperxml
<update id="updateBlog" parameterType="map">
update blog set author = #{author} where id = #{id}
</update>
- test
Map<String,Object> map = new HashMap<>();
map.put("id","123");
map.put("author","1234");
int i1 = mapper.updateBlog(map);
System.out.println(i1);
注意点:如果map使用@Param("map") Map<String,Object>来指定的话,sql中使用参数时要用map.key才能用,如果没有用@Param则不用直接使用key就可以了
配置之属性简介(mybatis-config.xml)
配置说明
- porperties:用于引入外部的配置文件
-
//用法一:引入外部的配置文件 <properties resource="jdbc.properties"/> //用法二:直接在configxml中配置 <properties> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/clt?useUnicode=true&characterEncoding=utf8&useSSL=true"/> </properties>
-
使用
//使用${}来引用配置文件中的值
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
- settings:用来配置其他的配置,如日志工厂
-
<settings> <!-- <setting name="logImpl" value="STDOUT_LOGGING"/>--> <setting name="logImpl" value="LOG4J"/> <!-- 开启数据库的user_id相对应的实体类的驼峰命名映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="cacheEnabled" value="true"/> </settings>
- typeAliases:用来给类配置别名,如pojo中的实体类
-
//用法一:将指定包下的类生成别名,别名为类的手字母小写 <typeAliases> <package name="POJO"/> </typeAliases> //用法二:将指定的类生成别名,别名为自定义 <typeAliases> <typeAlias type="" alias=""/> </typeAliases>
- environments:配合sqlsessionfactory的多个环境的容器
-
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
- environment:可以配置多个环境(但是sqlsessionfactory只能用一个,可以切换使用,如果有多个数据池)
-
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/clt?useUnicode=true"/> <property name="username" value="root"/> <property name="password" value="7536951"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"></dataSource> </environment> </environments>
- transactionmanager:配置事务管理器,mybatis默认为JDBC,还有其他的事务管理器
-
<transactionManager type="JDBC"/>
- dataSource:用于配置数据池,type可以指定其他的数据池
-
<dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/clt?useUnicode=true"/> <property name="username" value="root"/> <property name="password" value="7536951"/> </dataSource>
- mappers:配置绑定的mapperxml
-
//配置一:相对于类路径绑定指定的mapperxml <mappers> <mapper resource="com/chen/dao/daotestmapper.xml"/> </mappers> //配置二:使用绝对定位来绑定mapperxml <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers> //配置三:使用接口来进行绑定,但是接口名和mapperxml必须同名同包 <mappers> <mapper class="com.chen.dao.daotest"/> </mappers> //配置四:使用包扫描,但是接口名和mapperxml必须同名同包 <mappers> <package name="com.chen.dao"/> </mappers>
生命周期和作用域
- 生命周期和作用域是至关重要的,错误的使用会导致非常严重的并发问题
mybatis中的三个对象的生命周期和作用域
- sqlSessionFactoryBuilder:这个对象是用来创建sqlSessionFactory对象的,所以在创建完后就没有了,作用域为局部作用域。
- sqlSessionFactory:这个对象是生产sqlSession的,一旦创建了这个对象就同mybatis应用一同存在,且只能有一个,因此可以使用单例模式或者静态单例模式,作用域为应用级作用域
- sqlSession:这个对象不是线程安全的,不能被共享,需要用完即关,否则占用资源,引起宕机。作用域为方法或者一次请求。
resultmap结果集映射(对于实体类属性和字段不一样)
普通结果集映射
- 映射 :
<resultMap id="test" type="com.chen.pojo.Blog">
<result property="id" column="id"/>
<result property="title" column="title"/>
<result property="author" column="author"/>
<result property="time" column="time"/>
<result property="price" column="price"/>
</resultMap>
//id是给这个结果集映射做一个表示,方便引用
//type是指用于接收结果的实体类的类型
- 使用:
<select id="blogList" resultMap="test">
select * from blog
</select>
一对多结果集映射
- 映射:
//一对多按照结果处理,就是所要映射的数据都已经查出来了,比如两张表外连接
<resultMap id="test1" type="com.chen.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<result property="sex" column="tsex"/>
<collection property="studentList" javaType="ArrayList" ofType="com.chen.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
//一对多按照查询过程处理,就是所要映射的数据分开来查询,比如两张表分开查询,两个select语句分开
<resultMap id="teacherandstudent" type="com.chen.pojo.Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<collection property="studentList" javaType="ArrayList" ofType="com.chen.pojo.Student" column="id" select="student"/>
</resultMap>
<!-- 查询student-->
<select id="student" resultType="com.chen.pojo.Student">
//这边的id是从column的id传过来的,通过teache的id来查student
select * from student where tid=#{id}
</select>
- 使用:
<select id="getTeacher" resultMap="teacherandstudent">
select * from teacher
<!-- select s.name sname,s.id sid,t.id tid,t.name tname,t.sex tsex from student s left join teacher t on s.tid = t.id-->
</select>
- 说明:一对多使用collection来映射 javaType指对象属性的java类型如ArrayList,ofType是指集合中的对象类型
多对一结果集映射
- 映射:
//一对多按照结果处理,就是所要映射的数据都已经查出来了,比如两张表外连接
<resultMap id="test3" type="com.chen.pojo.Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="tid" column="tid"/>
<association property="teacher" javaType="com.chen.pojo.Student" column="tid" select="teacher1"/>
</resultMap>
<select id="teacher1" resultType="com.chen.pojo.Teacher">
select * from teacher where id = #{tid}
</select>
//这里查出来的student中的teacher没有查如果要查的话,可以嵌套查,只需要将teacher1改为resultMap为test4即可
<resultMap id="test4" type="com.chen.pojo.Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<collection property="studentList" ofType="com.chen.pojo.Student" column="id" select="s"/>
</resultMap>
<select id="s" resultType="com.chen.pojo.Student">
select * from student
</select>
- 使用:
<select id="getStudent" resultMap="test3">
select * from student
</select>
- 如果是一对多的话用collection来映射,如果是多对一使用association来映射
日志工厂(用于打印数据库sql和sql执行的步骤)
使用步骤
-
导入log4j的依赖包
//这个是最新版的log4j日志工厂 <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
配置log4j的配置文件
-
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/logFile.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
-
在mybatis-config配置settings配置项
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
测试结果
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl' adapter. [org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections. [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections. [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections. [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections. [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1800031768. [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6b4a4e18] [com.chen.dao.daotest.getStudent]-==> Preparing: select * from student [com.chen.dao.daotest.getStudent]-==> Parameters: [com.chen.dao.daotest.teacher1]-====> Preparing: select * from teacher where id = ? [com.chen.dao.daotest.teacher1]-====> Parameters: 1(Integer) [com.chen.dao.daotest.teacher1]-<==== Total: 1 [com.chen.dao.daotest.getStudent]-==> Preparing: select * from student [com.chen.dao.daotest.getStudent]-==> Parameters: [com.chen.dao.daotest.getStudent]-<== Total: 2 [Student{id=1, name='陈浪涛', tid=1, teacher=Teacher{id=1, name='陈老师', sex='男', studentList=null}}, Student{id=2, name='小红', tid=1, teacher=Teacher{id=1, name='陈老师', sex='男', studentList=null}}] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6b4a4e18] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6b4a4e18] [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1800031768 to pool.
log4j简介
- log4j是apache的一个开源项目,通过log4j可以控制日志信息输送的目的地是控制台,文件,GUI组件
- 可以控制每一条的日志的输出格式
- 通过定义每一条日志信息的级别,可以更加细致的控制日志的生成过程
- 通过一个配置文件来实现配置,不需要修改相应的代码
- 日志级别有:info,debug,error
- 使用日志级别输出信息需要在代码中新建一个Logger对象,调用里面的方法来进行调试 Logger logger = Logger.getLogger(Test.class); 通过get方法来实例化对象
mybatis实现分页
方法一:使用sql中的limit进行分页
-
工具类:可以计算出总页数,下一页,上一页
package cn.luxh.app.util; import java.io.Serializable; import java.util.List; /** * The <code>Pagination</code> class 分页 * * @author Luxh * @version 1.0 */ public class Pagination<T> implements Serializable{ private static final long serialVersionUID = 5104811017362151385L; /**当前页*/ private int currentPage; /**每页显示记录数*/ private int pageSize; /**总记录数*/ private long recordCount = 1L; /**记录集合*/ private List<T> recordList; /**总页数*/ private int pageCount; /**偏移数*/ private int offset; /**上一页*/ private int prePage; /**下一页*/ private int nextPage; /**是否有上一页*/ private boolean hasPrePage; /**是否有下一页*/ private boolean hasNextPage; /** * 默认的空参构造数 * */ public Pagination() { } /** * 构造函数,计算总页数、是否有上一页、下一页等. * @param currentPage 当前页 * @param pageSize 每页显示记录数 * @param recordCount 总记录数 * @param recordList 记录集合 */ public Pagination(int currentPage,int pageSize,long recordCount,List<T> recordList) { this.currentPage = currentPage; if(currentPage < 1) { this.currentPage = 1; } this.pageSize = pageSize; this.recordCount = recordCount; this.recordList = recordList; //上一页等于当前页减一 this.prePage = this.currentPage - 1; if(this.prePage < 1) { this.hasPrePage = false;//没有上一页 this.prePage = 1; }else { this.hasPrePage = true;//有上一页 } //计算总页数 this.pageCount = (int)Math.ceil(recordCount / (double)pageSize); if(this.currentPage > this.pageCount) { this.currentPage = this.pageCount; } //下一页等于当前页加一 this.nextPage = this.currentPage + 1; if(this.nextPage > this.pageCount) { this.hasNextPage = false;//没有下一页 this.nextPage = this.pageCount; }else { this.hasNextPage = true;//有下一页 } //偏移量 this.offset = (this.currentPage - 1)*pageSize; } public int getCurrentPage() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } public boolean isHasNextPage() { return hasNextPage; } public void setHasNextPage(boolean hasNextPage) { this.hasNextPage = hasNextPage; } public boolean isHasPrePage() { return hasPrePage; } public void setHasPrePage(boolean hasPrePage) { this.hasPrePage = hasPrePage; } public int getNextPage() { return nextPage; } public void setNextPage(int nextPage) { this.nextPage = nextPage; } public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public int getPageCount() { return pageCount; } public void setPageCount(int pageCount) { this.pageCount = pageCount; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getPrePage() { return prePage; } public void setPrePage(int prePage) { this.prePage = prePage; } public long getRecordCount() { return recordCount; } public void setRecordCount(long recordCount) { this.recordCount = recordCount; } public List<T> getRecordList() { return recordList; } public void setRecordList(List<T> recordList) { this.recordList = recordList; } }
-
limit公式:(nowpage-1)*pagesize,pagesize
方式二:使用java自带的rowbounds实现分页
方式三:使用mybatis中的pageHelper
方式四:前端也可以分页如Element-ui进行分页
mybatis使用注解开发(不用mapper.xml来实现接口)
步骤:
-
定义接口
-
直接在接口上使用注解实现CRUD
@Select("select * from blog") List<Blog> blogList();
-
通过class方式来绑定mybatis配置文件
<mapper class="com.chen.dao.daotest"/>
-
注意:使用注解来实现CRUD只适用于简单的sql语句
myatis执行流程剖析
自己看源码去
LomBok使用
步骤:
-
在IDEA中安装插件LomBok
-
导入lombok的依赖包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency>
几个注解说明:
- @Date:可以自动生成get,set,tostring,equals,有参构造
- @Getter:在类上使用可以生成所有的getter方法,在属性上使用,可以生成当前属性的setter方法
- @Setter:同上
- @AllArgsConstructor:生成有参构造
- @NoArgsConstructor:生成无参构造
mybatis实现动态sql
知道动态sql中常用的标签:
- where,set,if,choose,foreach,include,trim,bind
例子:
-
使用where和if实现动态sql
-
List<Blog> blogList2(@Param("map") Map<String,Object> map); <select id="blogList2" parameterType="map" resultType="com.chen.pojo.Blog"> select * from blog <where> <if test="map.id != null"> id = #{map.id} </if> <if test="map.title != null"> and title = #{map.title} </if> </where> </select> //不管有没有参数id和title,sql都能正常执行,如果id不为空,则sql加上where条件
-
-
使用set和if和where实现动态sql
-
int updateBlog(Map<String,Object> map) <update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> <where> <if test="id != null"> id = #{id} </if> </where> </set> </update>
-
-
使用choose,when,where实现动态sql
-
<select id="blogList2" parameterType="map" resultType="com.chen.pojo.Blog"> select * from blog <where> <choose> <when test="map.id != null"> id = #{map.id} </when> <when test="map.title != null"> title = #{map.title} </when> </choose> </where> </select> //choose相当于switch,满足一个条件就结束
-
-
使用foreach实现sql中的in效果
-
<select id="blogList2" parameterType="map" resultType="com.chen.pojo.Blog"> select * from blog <where> author in <foreach collection="map.arr" open="(" close=")" item="author" separator=","> #{author} </foreach> </where> </select> //sql语句:select * from blog WHERE author in ( ? , ? , ? )
-
trim可以自定义标签:如where和set
- 自定义where标签
<select id="blogList2" parameterType="map" resultType="com.chen.pojo.Blog">
select * from blog
<trim prefix="where" prefixOverrides="and">
author in
<foreach collection="map.arr" open="(" close=")" item="author" separator=",">
#{author}
</foreach>
</trim>
</select>
sql片段简介
-
<select id="blogList2" parameterType="map" resultType="com.chen.pojo.Blog"> select * from blog //使用include来引用sql片段,实现sql复用 <include refid="sqltest"></include> </select> //外面定义sql片段 <sql id="sqltest"> <trim prefix="where" prefixOverrides="and"> author in <foreach collection="map.arr" open="(" close=")" item="author" separator=","> #{author} </foreach> </trim> </sql>
mybatis的缓存简介
什么是缓存?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存中,用户查询数据就不用从磁盘上(关系型数据库)查询,直接从内存查询,从而提高查询的效率,解决高并发系统的性能问题。
为什么使用缓存?
- 减少和数据库的交互次数,减少系统的开销,提高系统效率。
什么样的数据能使用缓存?
- 需要经常查询,改变次数不多的数据
mybatis缓存
- mybatis包含了一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存,极大的提高查询效率。
- mybatis默认定义了两级缓存,有一级和二级缓存。默认情况下,只有一级缓存开启,也就是一次sqlsession,也称本地缓存。二级缓存需要手动开启和配置。他是基于一个namespace级别的缓存,为了提高拓展性,mybatis定义了缓存接口cache,我们可以通过实现这个接口来自定义二级缓存。
mybatis的缓存策略
- LRU:当缓存达到顶峰时候,将最长时间不使用的缓存移除
- FIFO:先进先出原则,先进来的先出去
- SOFT:软引用:基于垃圾回收器状态和软引用规则移除对象
- WEAK:弱引用:更积极的基于垃圾回收器状态和软引用规则移除对象
一级缓存
-
作用域:缓存只会存在一次sqlsession会话当中,也就是sqlsession开启到关闭
-
测试类:在一个测试类中连续2次查询相同的数据,发现只执行了一次sql语句。
-
[com.chen.dao.daotest.blogList]-==> Preparing: select * from blog [com.chen.dao.daotest.blogList]-==> Parameters: [com.chen.dao.daotest.blogList]-<== Total: 17 [Blog(id=0e5f55c5780345589da884efe43d952b, title=HelloSpring, author=陈浪涛01, time=Fri Jul 15 21:23:13 CST 2022, price=100), Blog(id=1, title=淘气包马小跳, author=clt1, time=Tue Jul 19 18:30:07 CST 2022, price=700), Blog(id=252223ce1ae04dea82bb551beb6256a8, title=HelloJava, author=陈浪涛大帅比, time=Fri Jul 15 21:23:13 CST 2022, price=100), Blog(id=2ec3228051fa4fddb231a4f5a875c643, title=HelloSpringMvc, author=陈浪涛02, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=5f1efc7a9ace496d864917e4f66d2874, title=HelloSpring, author=陈浪涛01, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=67a76c202ef64cc0948a6ba40b197a0c, title=HelloMybits, author=陈浪涛, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=6def317a0a734db98bd8b043208f5858, title=HelloMybits, author=陈浪涛03, time=Fri Jul 15 21:23:12 CST 2022, price=100), Blog(id=c8e60711210c46a1b8011fa910f82f3f, title=HelloJava, author=陈浪涛, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=da8068c38f9b4cc4b3169fa686a23081, title=HelloSpringMvc, author=陈浪涛02, time=Fri Jul 15 21:23:13 CST 2022, price=100), Blog(id=rfafdsg, title=淘气包马小跳, author=陈浪涛, time=Thu Jul 21 08:26:00 CST 2022, price=700), Blog(id=rfgadffahar, title=淘气包马小跳, author=陈浪涛, time=Thu Jul 21 08:45:46 CST 2022, price=700), Blog(id=rfgadffahardf, title=淘气包马小跳, author=陈浪涛, time=Thu Jul 21 08:51:31 CST 2022, price=700), Blog(id=rr, title=淘气包马小跳, author=陈浪涛, time=Tue Jul 19 18:39:17 CST 2022, price=700), Blog(id=rrr, title=淘气包马小跳, author=陈浪涛, time=Tue Jul 19 18:31:38 CST 2022, price=700), Blog(id=rrrrrr, title=淘气包马小跳, author=陈浪涛, time=Tue Jul 19 18:40:02 CST 2022, price=700), Blog(id=rrrrrrew, title=淘气包马小跳, author=陈浪涛, time=Wed Jul 20 09:46:18 CST 2022, price=700), Blog(id=rrrrrrewerr, title=淘气包马小跳, author=陈浪涛, time=Wed Jul 20 09:47:26 CST 2022, price=700)] [Blog(id=0e5f55c5780345589da884efe43d952b, title=HelloSpring, author=陈浪涛01, time=Fri Jul 15 21:23:13 CST 2022, price=100), Blog(id=1, title=淘气包马小跳, author=clt1, time=Tue Jul 19 18:30:07 CST 2022, price=700), Blog(id=252223ce1ae04dea82bb551beb6256a8, title=HelloJava, author=陈浪涛大帅比, time=Fri Jul 15 21:23:13 CST 2022, price=100), Blog(id=2ec3228051fa4fddb231a4f5a875c643, title=HelloSpringMvc, author=陈浪涛02, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=5f1efc7a9ace496d864917e4f66d2874, title=HelloSpring, author=陈浪涛01, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=67a76c202ef64cc0948a6ba40b197a0c, title=HelloMybits, author=陈浪涛, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=6def317a0a734db98bd8b043208f5858, title=HelloMybits, author=陈浪涛03, time=Fri Jul 15 21:23:12 CST 2022, price=100), Blog(id=c8e60711210c46a1b8011fa910f82f3f, title=HelloJava, author=陈浪涛, time=Sat Jul 16 12:21:34 CST 2022, price=100), Blog(id=da8068c38f9b4cc4b3169fa686a23081, title=HelloSpringMvc, author=陈浪涛02, time=Fri Jul 15 21:23:13 CST 2022, price=100), Blog(id=rfafdsg, title=淘气包马小跳, author=陈浪涛, time=Thu Jul 21 08:26:00 CST 2022, price=700), Blog(id=rfgadffahar, title=淘气包马小跳, author=陈浪涛, time=Thu Jul 21 08:45:46 CST 2022, price=700), Blog(id=rfgadffahardf, title=淘气包马小跳, author=陈浪涛, time=Thu Jul 21 08:51:31 CST 2022, price=700), Blog(id=rr, title=淘气包马小跳, author=陈浪涛, time=Tue Jul 19 18:39:17 CST 2022, price=700), Blog(id=rrr, title=淘气包马小跳, author=陈浪涛, time=Tue Jul 19 18:31:38 CST 2022, price=700), Blog(id=rrrrrr, title=淘气包马小跳, author=陈浪涛, time=Tue Jul 19 18:40:02 CST 2022, price=700), Blog(id=rrrrrrew, title=淘气包马小跳, author=陈浪涛, time=Wed Jul 20 09:46:18 CST 2022, price=700), Blog(id=rrrrrrewerr, title=淘气包马小跳, author=陈浪涛, time=Wed Jul 20 09:47:26 CST 2022, price=700)] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@16e7dcfd]
-
-
缓存失效:
- 手动清理啦缓存:sqlsession.clear()
- 增删改会刷新原来的缓存数据,以确保数据的正确性
- 查询不同mapper.xml下的数据
二级缓存
- 简介:二级缓存也叫全局缓存,一级缓存的作用域太低了,所以诞生了二级缓存,基于namespace级别的缓存,一个名称空间对应一个二级缓存。
- 工作机制:
- 一次会话查询数据,这个数据就会被放在当前的会话的一级缓存中
- 如果当前的会话关闭了,这个会话对应的一级缓存也就没了,但开启了二级缓存后,一级缓存的数据就会被保存到二级缓存中
- 只要是在同一个mapper下的方法查询相同的数据,如果二级缓存中有,就直接从二级缓存中取。
- 使用二级缓存的步骤
- 首先要在mybatis-config中手动开启二级缓存,默认就开启的
- 然后在mapper.xml中开启二级缓存
- 最后注意实体类要序列化才能被保存在缓存中,只需要实现Serializable接口即可
- 测试类:通过两个sqlsession对象,调用同一个mapper.xml中的方法发现只有一个slq语句执行了
- 首先要在mybatis-config中手动开启二级缓存,默认就开启的
mybatis缓存的原理
缓存查找顺序
-
用户查找数据,会先到二级缓存中查找,如果没有,再到一级缓存中查找,如果还是没有,才会到数据库中查找,查到之后,sqlsession关闭,一级缓存中的数据到二级缓存中。如果还有用户要查找相同的数据,直接到二级缓存中拿即可。
-
<select id="blogList2" parameterType="map" resultType="com.chen.pojo.Blog" useCache="true" flushCache="false"> select * from blog <include refid="sqltest"></include></select> //注意:开启了二级缓存后,所有的数据都会存在二级缓存中,如果某个数据不想存在二级缓存中,可以在后面添加useCache是否使用二级缓存,flushCache是否在增删改刷新缓存数据,调优的时候可以使用
EHcache第三方缓存
使用步骤
-
导包
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.2</version> </dependency>
-
配置ehcache配置文件(EHcache.xml)
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 --> <diskStore path="java.io.tmpdir/Tmp_EhCache"/> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> </ehcache>
-
在mapper.xml中配置使用第三方缓存策略
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
EHcache简介
- 他是一种广泛使用的开源java分布式缓存,主要面向通用缓存
自定义缓存策略:通过实现Cache接口即可