Mybatis

Mybatis

一、简介

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO 映射成数据库中的记录。

二、Mybatis配置

核心日志文件Mybatis-config.xml

各个配置项需要按照以下顺序进行配置

在这里插入图片描述

1.properties属性

数据源相关属性可以在外部进行配置,并可以进行动态替换

创建db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=1234

修改核心配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties" />
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!--driver、url、username、password会从db.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>
        </environment>
    </environments>
</configuration>

也可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值,eg:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//传入配置项(错误密码),动态覆盖
Properties properties = new Properties();
properties.setProperty("password", "root");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream, properties);

//java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)

属性优先级(从高到低):

  1. 通过SqlSessionFactoryBuilder.build() 方法参数传递的属性
  2. resource / url属性中指定的配置文件
  3. properties 元素中指定的属性

给属性配置默认值:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties">
        <!--开启占位符指定默认值特性-->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" 			value="true"/>
	</properties>
    <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}"/>
 <!--给密码属性设置默认值1234,如果db.properties文件中没有指定password属性则使用默认值-->
                <property name="password" value="${password?:1234}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
2.settings设置

常用的设置:

设置名描述默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存true
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从数据库列名A_COLUMN 映射 Java 属性名 aColumnfalse
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)false
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找.未设置
3.typeAliases类型别名

修改mybatis-config.xml

<configuration>
	......
    <typeAliases>
        <typeAlias type="com.hubu.mybatisDemo.pojo.User" alias="User" />
    </typeAliases>
    ......
</configuration>

修改UserMapper.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="com.hubu.mybatisDemo.mapper.UserMapper">
    <select id="getUserList" resultType="User">
        select * from user
    </select>

    <select id="getUserByUsername" parameterType="String" resultType="User">
        select * from mybatis.user where username = #{username}
    </select>

    <insert id="addUser" parameterType="User">
        insert into mybatis.user values(#{username}, #{password})
    </insert>

    <delete id="deleteUser" parameterType="String">
        delete from mybatis.user where username = #{username}
    </delete>

    <update id="updateUser" parameterType="User">
        update mybatis.user set password = #{password} where username = #{username}
    </update>
</mapper>
4.environments环境配置

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中

尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

<configuration>
	......
    <!--默认使用的环境ID(比如:default="development")-->
    <environments default="development">
        <!--环境id为deveploment-->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="..."/>
                <property name="url" value="..."/>
                <property name="username" value="..."/>
                <property name="password" value="..."/>
            </dataSource>
    	</environment>
        
        <!--第二套环境-->
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="..."/>
                <property name="url" value="..."/>
                <property name="username" value="..."/>
                <property name="password" value="..."/>
            </dataSource>
    	</environment>
    </environments>
    ......
</configuration>
  • default属性表示默认使用的环境 ID(比如:default=“development”)
  • 每个 environment 元素定义环境 ID(比如:id=“development”)

有两种类型的事务管理器(即 type=“[JDBC|MANAGED]”):

  • JDBC – 使用了 JDBC 的提交和回滚机制,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么,它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期,默认情况下它会关闭连接。

有三种内建的数据源类型(即 type=“[UNPOOLED|POOLED|JNDI]”):

  • UNPOOLED – 没有连接池,每次请求时打开和关闭连接。
  • POOLED – 利用池将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间,能使并发 Web 应用快速响应请求。
  • JNDI – 能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
5.mappers映射器

告诉 MyBatis 到哪里去找到 SQL 映射语句

1.使用相对于类路径的资源引用(推荐使用)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	......
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>
2. 使用映射器接口实现类的完全限定类名

接口名称必须和xml名称相同并在同一个包下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	......
    <mappers>
        <mapper class="com.qingsongxyz.mapper.UserMapper.xml"/>
    </mappers>
</configuration>
3.将包内的映射器接口实现全部注册为映射器

接口名称必须和xml名称相同并在同一个包下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	......
    <mappers>
          <package name="com.qingsongxyx.mapper"/>
    </mappers>
</configuration>

三、日志

可选值:

SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING 、STDOUT_LOGGING、

NO_LOGGING

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	......
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>
	......
</configuration>

创建log4j.properties:

log4j.rootLogger = debug,stdout,D,E

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = .log/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = .log/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

在这里插入图片描述

static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void testLog4j(){
    logger.info("info------");
    logger.debug("debug-----");
    logger.error("error-----");
}

测试:

在这里插入图片描述

四、XML映射器

常用属性:

属性描述
id对应接口中的方法名称
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)
resultType从这条Sql语句中返回结果的类全限定名或别名,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。resultType 和 resultMap 之间只能同时使用一个
resultMap对外部 resultMap 的命名引用
1.select

映射插入语句

<select id="getStudentList" resultType="Student">
    select * from student
</select>
2.insert

映射插入语句

<insert id="addStudent" parameterType="Student">
    insert into student(sno, name, age, tno) values(#{sno}, #{name}, #{age}, #{tno})
</insert>
3.update

映射更新语句

<update id="updateStudent" parameterType="Student">
    update student set name = #{name}, age = #{age}, tno = #{tno} where sno = #{sno}
</update>
4.delete

映射删除语句

<delete id="deleteStudent" parameterType="String">
    delete from student where sno = #{sno}
</delete>
5.sql

定义可重用的 SQL 代码片段,以便在其它语句中使用

<sql id="studentColumns">
    sno, name, age
</sql>

<select id="getStudentList" resultType="Student">
    select
    <!--在 include 元素的 refid 属性或内部语句中设置属性值-->
    <include refid="studentColumns"/>
    from student
</select>
*6.resultMap

解决数据库字段名和实体类属性名不一致:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String username;
    private String pwd;
}
<resultMap id="Users" type="User">
   	<!--数据库字段名为password,实体类属性名为pwd-->
    <result property="pwd" column="password"/>
</resultMap>

<select id="getUserList" resultMap="Users">
    select * from user
</select>

解决返回值类型与实体类不一致(一对多、多对一问题):

实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private String tno;
    private String name;
    //表示班主任的学生,一个班主任有多个学生
    private List<Student> students;

    public Teacher(String tno, String name) {
        this.tno = tno;
        this.name = name;
    }
}
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Student {
    private String sno;
    private String name;
    private int age;
    //表示该学生对应的班主任, 一个学生只有一个班主任
    private Teacher teacher;

    public Student(String sno, String name, int age, String tno) {
        this.sno = sno;
        this.name = name;
        this.age = age;
    }
}

一对多:

使用集合collection

属性描述
javaType一个 Java 类的完全限定名,或一个类型别名
jdbcTypeJDBC 类型
ofType将 JavaBean(或字段)属性的类型和集合存储的类型区分开来

MyBatis 通过内置的 jdbcType 支持的 JDBC 类型:

BITFLOATCHARTIMESTAMPOTHERUNDEFINED
TINYINTREALVARCHARBINARYBLOBNVARCHAR
SMALLINTDOUBLELONGVARCHARVARBINARYCLOBNCHAR
INTEGERNUMERICDATELONGVARBINARYBOOLEANNCLOB
BIGINTDECIMALTIMENULLCURSORARRAY
public interface  TeacherMapper {
    
    //查询一个老师对应的所有学生的信息
    Teacher getTeacherStudents(String tno);
}
<?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="com.hubu.mybatisDemo.mapper.TeacherMapper">
    <!-- 查询一个老师对应的所有学生的信息 -->
    <resultMap id="getStudents" type="Teacher">
        <id property="tno" column="tno"/>
        <result property="name" column="tname"/>
        <collection property="students" javaType="ArrayList" ofType="Student">
            <id property="sno" column="sno"/> 
            <result property="sno" column="sno"/>
            <result property="name" column="sname"/>
            <result property="age" column="sage"/>
        </collection>
    </resultMap>

    <select id="getTeacherStudents" resultMap="getStudents">
        select
            s.sno as sno,
            s.name as sname,
            s.age as sage,
            t.tno,
            t.name as tname
        from student s, teacher t
        where s.tno = t.tno and t.tno = #{tno};
    </select>
</mapper>

测试:

@Test
public void testGetStudents(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacherStudents("1");
    System.out.println(teacher);
    sqlSession.close();
}

在这里插入图片描述

多对一:

使用关联association

public interface StudentMapper {
    
    //查询单行数据
    Student getStudentBySno(String sno);
}
<?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="com.hubu.mybatisDemo.mapper.StudentMapper">
        
    <!-- 查询每个学生对应的老师信息 -->
    <resultMap id="getStudentTeacherInfo" type="Student">
        <result property="sno" column="sno"/>
        <result property="age" column="sage"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <id property="tno" column="tno"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>
    
    <select id="getStudentBySno" parameterType="String" resultMap="getStudentTeacherInfo">
        select s.sno as sno,
               s.age as sage,
               s.name as sname,
               t.name as tname,
               t.tno
        from student s, teacher t
        where s.sno = t.tno and s.sno = #{sno}
    </select>
</mapper>

测试:

@Test
public void testSelectBySno(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student student = mapper.getStudentBySno("1");
    System.out.println(student);
    sqlSession.close();
}

在这里插入图片描述

五、动态Sql

根据不同的条件生成不同的sql语句

1.if

test属性中的条件满足时拼接sql语句

//查询所有数据
List<Course> getCourseList(String cno);
<select id="getCourseList" parameterType="String" resultType="Course">
    select * from course
    <where>
        <if test="cno != null">
            cno = #{cno}
        </if>
    </where>
</select>
@Test
public void testSelect()
{
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
    //List<Course> courseList = mapper.getCourseList(null);
    List<Course> courseList = mapper.getCourseList("1");
    for (Course course : courseList) {
        System.out.println(course);
    }
    sqlSession.close();
}

注意:

where 元素只会在子元素返回任何内容的情况下才插入。而且,若子句的开头为 “AND” 或 “OR”,where

素也会将它们去除。

在这里插入图片描述

在这里插入图片描述

2.choose(when,otherwise)

choose和when、otherwise一起使用,多个条件中有一个满足则忽略后面的条件

//查询单行数据
Course getOneCourse(Course course);
<select id="getOneCourse" parameterType="Course" resultType="Course">
    select * from course
    <where>
        <choose>
            <when test="cno != null">
                cno = #{cno}
            </when>
            <when test="cname != null">
                cname = #{cname}
            </when>
            <when test="sno != null">
                sno = #{sno}
            </when>
            <when test="sname != null">
                sname = #{sname}
            </when>
            <when test="tno != null">
                tno = #{tno}
            </when>
            <when test="tname != null">
                tname = #{tname}
            </when>
        </choose>
    </where>
</select>
@Test
public void testGetOneCourse() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
    //Course courseByCno = mapper.getOneCourse(new Course(null, "心理学", "1", null, null, null));
    Course courseByCno = mapper.getOneCourse(new Course(null, null, "1", null, null, null));
    System.out.println(courseByCno);
    sqlSession.close();
}

在这里插入图片描述

在这里插入图片描述

3.trim(where、set)

where 元素等价的自定义 trim:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(空格是必要的),会移除所有属性中指定的内容,

并且插入 prefix 属性中指定的内容。

//修改数据
int updateCourse(Course course);
<update id="updateCourse" parameterType="Course">
    update course
    <set>
        <if test="cname != null">cname = #{cname},</if>
        <if test="sname != null">sname = #{sname},</if>
        <if test="tname != null">tname = #{tname}</if>
    </set>
    where cno = #{cno} and sno = #{sno} and tno = #{tno}
</update>
@Test
public void testUpdateCourse() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
    int affectedRows = mapper.updateCourse(new Course("2", "Linux", "1", "李四", "1", "徐婕"));
    System.out.println(affectedRows > 0 ? "操作成功" : "操作未影响到表");
    sqlSession.commit();
    sqlSession.close();
}

在这里插入图片描述

set 元素等价的自定义 trim

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

去除不必要的suffixOverrides属性中指定的内容

4.foreach

对任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象进行遍历,index 是当前迭代的序号,item 的值

是本次迭代获取到的元素,collection是迭代的对象,open为前缀,close为后缀,separator是分隔符

List<Course> getCourse(int[] arr);
<select id="getCourse" resultType="Course">
    select * from course
    <where>
        <foreach item="cno" collection="array"
              open="" separator=" or " close="">
            cno = #{cno}
        </foreach>
    </where>
</select>
@Test
public void testGetCourse() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
    int[] arr = {1, 2};
    List<Course> courses = mapper.getCourse(arr);
    for (Course cours : courses) {
        System.out.println(cours);
    }
    sqlSession.close();
}

在这里插入图片描述

六、CRUD

导入依赖:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
</dependency>

创建用户表:

CREATE TABLE `user` (
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(50) NOT NULL COMMENT '密码',
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建实体类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String username;
    private String password;
}

编写UserMapper接口:

public interface UserMapper {
    //查询所有数据
    List<User> getUserList();

    //查询单行数据
    User getUserByUsername(String username);

    //增加数据
    int addUser(User user);

    //删除数据
    int deleteUser(String username);

    //修改数据
    int updateUser(User user);
}

编写UserMapper.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="com.hubu.mybatisDemo.mapper.UserMapper">
    <select id="getUserList" resultType="com.hubu.mybatisDemo.pojo.User">
        select * from user
    </select>

    <select id="getUserByUsername" parameterType="String" resultType="com.hubu.mybatisDemo.pojo.User">
        select * from mybatis.user where username = #{username}
    </select>

    <insert id="addUser" parameterType="com.hubu.mybatisDemo.pojo.User">
        insert into mybatis.user values(#{username}, #{password})
    </insert>

    <delete id="deleteUser" parameterType="String">
        delete from mybatis.user where username = #{username}
    </delete>

    <update id="updateUser" parameterType="com.hubu.mybatisDemo.pojo.User">
        update mybatis.user set password = #{password} where username = #{username}
    </update>    
    
    <select id="getUserList" resultType="com.hubu.mybatisDemo.pojo.User">
        select * from user
    </select>

    <select id="getUserByUsername" parameterType="String" resultType="com.hubu.mybatisDemo.pojo.User">
        select * from mybatis.user where username = #{username}
    </select>

    <insert id="addUser" parameterType="com.hubu.mybatisDemo.pojo.User">
        insert into mybatis.user values(#{username}, #{password})
    </insert>

    <delete id="deleteUser" parameterType="String">
        delete from mybatis.user where username = #{username}
    </delete>

    <update id="updateUser" parameterType="com.hubu.mybatisDemo.pojo.User">
        update mybatis.user set password = #{password} where username = #{username}
    </update>
</mapper>

编写mybatis核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://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/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

编写工具类MybatisUtils用于获取SqlSession:

public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static{
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

测试:

public class UserMapperTest {
    
    @Test
    public void testSelect(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

    @Test
    public void testSelectByUsername(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User admin = mapper.getUserByUsername("admin");
        System.out.println(admin);
        sqlSession.close();
    }

    @Test
    public void testAdd(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int affectedRows = mapper.addUser(new User("zhao", "123456"));
        System.out.println(affectedRows);
        sqlSession.commit(); //添加数据需要提交事务
        sqlSession.close();
    }

    @Test
    public void testDelete(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int affectedRows = mapper.deleteUser("tom");
        System.out.println(affectedRows);
        sqlSession.commit(); //删除数据需要提交事务
        sqlSession.close();
    }

    @Test
    public void testUpdate(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int affectedRows = mapper.updateUser(new User("admin", "123456"));
        System.out.println(affectedRows);
        sqlSession.commit(); //修改数据需要提交事务
        sqlSession.close();
    }
}

/*
selectAll:
User(username=admin, password=admin)
User(username=root, password=root)
User(username=zhaosong, password=123456)
----------------------------------
selectOne:
User(username=admin, password=admin)
----------------------------------
add:
1
---------------------------------
delete:
1
--------------------------------
update:
1
*/

七、Mybatis缓存

作用域和生命周期:

SqlSessionFactoryBuilder:

SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域即局部变量作用域,一旦创建了 SqlSessionFactory,就不再需要它了。

SqlSessionFactory:

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,最佳作用域是应用作用域(Application),最简单的就是使用单例模式或者静态单例模式。

SqlSession:

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求作用域(Request)。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

1.一级缓存

同一个SqlSession查询的数据会被缓存,mybatis默认开启一级缓存

@Test
public void testCache(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList1 = mapper.getUserList();
    List<User> userList2 = mapper.getUserList();
    for (User user : userList1) {
        System.out.println(user);
    }
    System.out.println("=======================================");
    for (User user : userList2) {
        System.out.println(user);
    }
    System.out.println("userList1 == userList2 : " + (userList1 == userList2));
    sqlSession.close();
}

在这里插入图片描述

一级缓存失效情况:

  1. 不同SqlSession查询条件相同
  2. 同一个SqlSession查询条件不同
  3. 同一个SqlSession两次相同查询之间进行了一次增删改操作
  4. 同一个SqlSession两次相同查询之间手动清空缓存
@Test
public void testCache2(){
    SqlSession sqlSession1 = MybatisUtils.getSqlSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = mapper1.getUserList();
    for (User user : userList1) {
        System.out.println(user);
    }
    sqlSession1.close();
    System.out.println("=======================================");
    SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    List<User> userList2 = mapper2.getUserList();
    for (User user : userList2) {
        System.out.println(user);
    }
    System.out.println("userList1 == userList2 : " + (userList1 == userList2)); //false
    sqlSession2.close();
}
@Test
public void testCache(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList1 = mapper.getUserList();
    for (User user : userList1) {
        System.out.println(user);
    }
    //sqlSession.clearCache(); //手动清空缓存
    int i = mapper.addUser(new User("asd1","asd")); //进行了一次增加操作会更新缓存
    System.out.println("=======================================");
    List<User> userList2 = mapper.getUserList();
    for (User user : userList2) {
        System.out.println(user);
    }
    System.out.println("userList1 == userList2 : " + (userList1 == userList2));
    sqlSession.close();
}

在这里插入图片描述

在这里插入图片描述

2.二级缓存

同一个SqlSessionFactory创建的SqlSession查询结果会被缓存,两次查询之间进行了一次增删改操作会使一

级缓存和二级缓存都失效

开启二级缓存:

在mapper.xml文件中声明

<cache />

配置项:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

eviction属性指定清除策略,默认为LRU

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

flushInterval指定刷新间隔,以毫秒为单位

size指定引用数目。默认值是 1024。

readOnly指定是否可读,只读的缓存会返回缓存对象,不能被修改性能更高。而可读写的缓存会(通过序列化)

返回缓存对象的拷贝,速度上会慢一些,但是更安全,因此默认值是 false。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private String username;
    private String password;
}
@Test
public void testCache2(){
    SqlSession sqlSession1 = MybatisUtils.getSqlSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = mapper1.getUserList();
    for (User user : userList1) {
        System.out.println(user);
    }
    //二级缓存需要在Sqlsession关闭或提交之后才有效
    sqlSession1.close();
    System.out.println("=======================================");
    SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    //当二级缓存设置为读写缓存时,查询数据转换的实体类需要实现序列化接口
    List<User> userList2 = mapper2.getUserList();
    for (User user : userList2) {
        System.out.println(user);
    }
    sqlSession2.close();
}

在这里插入图片描述

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值