• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
在赶路的我
博客园    首页    新随笔    联系   管理    订阅  订阅

MyBatis

Mybatis


什么是mybatis?

  • mybatis是一款优秀的持久化框架
  • 历史:mybatis是apache的一个开源项目ibatis,2010年apache将他迁移到goodle code并改名为Mybatis,2013年迁移到github

为什么学习mybatis?

  • 帮助我们将数据存到数据库中
  • 传统的jdbc代码复杂,mybatis简化

优点

  • 实现了sql和代码分开,提高了代码的可维护性。
  • 提供了映射标签,支持对象关系映射
  • 提供xml文件,支持动态sql
  • 使用的人多

第一个mybatis程序


使用步骤

  1. 导入依赖
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
  1. 配置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>
  1. 用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>
  1. 构建工具类
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();
    }
}

  1. 开启测试
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);
        }
    }
}

  1. 测试结果
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中的对应

  1. mapper接口中参数的名字和sql中的一样,且mapper中要配置paramtertype
  2. 在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)


配置说明

  1. 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&amp;characterEncoding=utf8&amp;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>
  1. 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>
    
  1. typeAliases:用来给类配置别名,如pojo中的实体类
  •     
    	//用法一:将指定包下的类生成别名,别名为类的手字母小写
      	<typeAliases>
            <package name="POJO"/>
        </typeAliases>
        //用法二:将指定的类生成别名,别名为自定义
        <typeAliases>
            <typeAlias type="" alias=""/>
        </typeAliases>
    
  1. 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>
    
  1. 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>
    
  1. transactionmanager:配置事务管理器,mybatis默认为JDBC,还有其他的事务管理器
  • <transactionManager type="JDBC"/>
    
  1. 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>
    
  1. 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中的三个对象的生命周期和作用域

  1. sqlSessionFactoryBuilder:这个对象是用来创建sqlSessionFactory对象的,所以在创建完后就没有了,作用域为局部作用域。
  2. sqlSessionFactory:这个对象是生产sqlSession的,一旦创建了这个对象就同mybatis应用一同存在,且只能有一个,因此可以使用单例模式或者静态单例模式,作用域为应用级作用域
  3. 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执行的步骤)


使用步骤

  1. 导入log4j的依赖包

            //这个是最新版的log4j日志工厂
    		<dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
  2. 配置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
      
      
  3. 在mybatis-config配置settings配置项

        <settings>
            <setting name="logImpl" value="LOG4J"/>
        </settings>
    
  4. 测试结果

    [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使用


步骤:

  1. 在IDEA中安装插件LomBok

  2. 导入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

例子:

  1. 使用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条件
      
  2. 使用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>
      
  3. 使用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,满足一个条件就结束
      
  4. 使用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的缓存简介


什么是缓存?

  1. 存在内存中的临时数据
  2. 将用户经常查询的数据放在缓存中,用户查询数据就不用从磁盘上(关系型数据库)查询,直接从内存查询,从而提高查询的效率,解决高并发系统的性能问题。

为什么使用缓存?

  • 减少和数据库的交互次数,减少系统的开销,提高系统效率。

什么样的数据能使用缓存?

  • 需要经常查询,改变次数不多的数据

mybatis缓存

  1. mybatis包含了一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存,极大的提高查询效率。
  2. mybatis默认定义了两级缓存,有一级和二级缓存。默认情况下,只有一级缓存开启,也就是一次sqlsession,也称本地缓存。二级缓存需要手动开启和配置。他是基于一个namespace级别的缓存,为了提高拓展性,mybatis定义了缓存接口cache,我们可以通过实现这个接口来自定义二级缓存。

mybatis的缓存策略

  1. LRU:当缓存达到顶峰时候,将最长时间不使用的缓存移除
  2. FIFO:先进先出原则,先进来的先出去
  3. SOFT:软引用:基于垃圾回收器状态和软引用规则移除对象
  4. 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缓存的原理

缓存查找顺序

  • 用户查找数据,会先到二级缓存中查找,如果没有,再到一级缓存中查找,如果还是没有,才会到数据库中查找,查到之后,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第三方缓存

使用步骤

  1. 导包

            <dependency>
                <groupId>org.mybatis.caches</groupId>
                <artifactId>mybatis-ehcache</artifactId>
                <version>1.2.2</version>
            </dependency>
    
  2. 配置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>
    
  3. 在mapper.xml中配置使用第三方缓存策略

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

EHcache简介

  • 他是一种广泛使用的开源java分布式缓存,主要面向通用缓存

自定义缓存策略:通过实现Cache接口即可

posted @ 2023-02-25 16:41  在赶路的我  阅读(12)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3