首页 > 其他分享 >Mybatis

Mybatis

时间:2023-01-20 17:57:07浏览次数:61  
标签:缓存 log4j mybatis sqlSession Mybatis where id

持久层框架

  • 持久化:持久化就是将程序的数据再持久状态和瞬时状态

  • 持久化方法:数据库(jdbc)、io文件持久化

  • 内存:断电即失

  • 持久层:完成持久层的代码 Dao层,Service层,Controller层

第一个Mybatis程序

搭建环境

父工程导包:Mybatis,mysql-connector-java,junit

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

子工程会自动继承父工程的包

创建一个模块

子工程resources文件夹下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"/>
                <!--注意下面的&无法识别,需要使用&amp;进行转义-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="密码"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration> 

使用Mybatis第一部:获取sqlSessionFactory对象(代码来自于官方文档)

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

既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

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

编写代码

  • 实体类(pojo)
package com.zaughter.pojo;

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

  • Dao接口(以后按照官网要求命名为mapper)
package com.zaughter.dao;

import com.zaughter.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> getUserList();
}
  • 接口实现类(由原来的UserDaoImp.java文件转为UserMapper.xml配置文件)
<?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">
<!--namespace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.zaughter.dao.UserDao">
    <!--select查询语句,id对应接口里的方法名,resultType对应类型-->
    <select id="getUserList" resultType="com.zaughter.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

测试(junit)

maven导出资源问题

注意点:maven由于他的约定大于配置,可能会遇到写的配置文件无法被导出或者生效的问题(xml文件没有放在resources文件夹下导致未被扫描),解决方法:在build中配置resources

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
    </resources>
</build>
  1. 只在父工程中放置就可以成功
  2. 一般resources放在java上面
  3. 如果要写filtering,要用false。如果父工程中filtering用了true,需要在子工程中也配置(不能用true)
  4. 如果使用utf-8,xml中不要有中文注释。utf-8与utf8有区别,在MySQL数据库中只能使用“utf8”
  5. useSSL的选择也有讲究,这里我是用true正常,网上有些人说他们的需要改成false(版本兼容导致的)

测试代码

package com.zaughter.dao;

import com.zaughter.pojo.User;
import com.zaughter.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserMapperTest {
    @Test
    public void test(){
        //获得sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //执行SQL方式一:getMapper
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();

        //执行SQL方式二:老方法,返回类型不安全
        List<User> userList1 = sqlSession.selectList("com.zaughter.dao.UserMapper.getUserList");


        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

CRUD

namespace

namespace中的包名要与Dao/Mapper接口的包名一致

select

  • id:就是对应的namespace中的方法名
  • resultType:Sql语句执行的返回值类型
  • parameterType:参数类型

增的例子

UserMapper.java接口

int updateUser(User user);//返回值为被影响行数

UserMapper.xml实现

<insert id="addUser" parameterType="com.zaughter.pojo.User">
    <!--由于在参数类型里指定了是实体类User,所以下面values可以直接调用User中的私有属性-->
    insert into mybatis.user(id, name, pwd) values (#{id},#{name},#{pwd})
</insert>

UserMapperTest.java测试

@Test
public void addUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.addUser(new User(4,"zhahh","123345"));
    //增删改最后要提交事务才能成功
    sqlSession.commit();
    sqlSession.close();
}

Map

假设实体类或者数据库中的表字段参数过多,我们应当考虑使用Map

add例子:

UserMapper.java接口

int addUser2(Map<String,Object> map);

UserMapper.xml实现

使用Map方式,在values处变量名可以随意命名而不需要使用User类里已经定义好的属性名。并且values不需要将User所有属性都列出来,可以根据需要选择传入的参数。对于查询语句更有用(可以通过and实现多条件查询)

<insert id="addUser2" parameterType="map">
    insert into mybatis.user(id, pwd) values (#{userId},#{password})
</insert>

UserMapperTest.java测试

@Test
public void addUser2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("userId",5);
    map.put("password","123123123");
    mapper.addUser2(map);
    //增删改最后要提交事务才能成功
    sqlSession.commit();
    sqlSession.close();
}

Map传递参数,直接在sql中取出key即可【parameterType="map"】

对象传递参数,直接在sql中取出对象的属性即可【parameterType="对象"】

只有一个基本类型参数,可以直接在sql中取到

多个参数用Map或者注解

配置解析

核心配置文件

  • mybatis-config.xml

  • 各种标签是由先后顺序的,顺序错了会报错
    configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

环境配置(environments)

Mybatis默认事务管理器(transactionManager type)是JDBC,数据源类型(dataSource type)默认POOLED

属性(properties)

通过properties属性来实现引用配置文件

方式一:写死

<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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

方式二:通过properties标签读取

<!--读取同文件夹下的db.properties配置文件,具体的内容都写在db文件中,这里用了自闭和-->
<!--注意db.properties中&不需要转义-->
<properties resource="db.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}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

方式三:一二结合

<!--通过properties读取配置文件的同时,可以通过方式一添加一些配置文件里没有的内容-->
<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</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}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

如果外部配置文件中有某一属性,又在标签中写了这个属性。优先读取外部配置文件中的属性内容

类型别名(typeAliases)

方法一

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。原来的接口实现文件中<insert id="addUser" parameterType="com.zaughter.pojo.User">参数类型要写全部路径,现在可以通过别名简短代码

<typeAliases>
  <typeAlias type="com.zaughter.pojo.User"  alias="User"/>
</typeAliases>

方法二

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,扫描实体类的包,他的默认别名就是类名(这时候不区分大小写 )

<typeAliases>
  <package name="com.zaughter.pojo"/>
</typeAliases>

如果在此基础上,在User类上使用Alias注解,则他的别名是注解中定义的别名

有一些Java类型内建的类型别名,可以直接使用

别名 映射的类型
_byte byte
_char (since 3.5.10) char
_character (since 3.5.10) char
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
char (since 3.5.10) Character
character (since 3.5.10) Character
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
biginteger BigInteger
object Object
date[] Date[]
decimal[] BigDecimal[]
bigdecimal[] BigDecimal[]
biginteger[] BigInteger[]
object[] Object[]
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

设置

主要需要记忆的

映射器(mappers)

在通过一个xml文件实现接口后,需要在配置文件中注册Mapper文件

方式一 直接指向配置文件

<mappers>
    <mapper resource="com/zaughter/dao/UserMapper.xml"></mapper>
</mappers>

方式二 使用class文件绑定注册(需要接口和Mapper文件在同一个目录下)

<mappers>
    <mapper class="com.zaughter.dao.UserMapper"></mapper>
</mappers>

方式三 扫描整个包

<mappers>
    <package name="com.zaughter.dao"/>
</mappers>

作用域(Scope)和生命周期

SqlSessionFactoryBuilder

  • 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
  • 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
  • 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在
  • 可以想象为一个数据库连接池
  • 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,因此 SqlSessionFactory 的最佳作用域是应用作用域
  • 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • 每个线程都应该有它自己的 SqlSession 实例
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
  • 用完之后需要关闭,防止资源被占用

解决属性名和字段名不一致的问题

如果字段名为pwd,而属性名为password。在Mapper中使用select * from mybatis.user where id = #{id}会导致密码数据的查询结果为空

低级方法

将*改为手写代码select id,name,pwd from mybatis.user where id = #{id}

使用resultMap结果集映射

将标签中的resultType改成resultMap(映射)

使用resultMap标签定义结果集映射

<!--id是映射的名字,type是映射的实体类(这里User用了别名)-->
<resultMap id="UserMap" type="User">
    <!--colum是数据库字段名,property是实体类中的属性。这里其实只用写不一致的就可以-->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

日志

日志工厂

用来方便排错

在核心配置文件中利用setting标签设置,标准的日志工厂的实现<setting name="logImpl" value="STDOUT_LOGGING"/>

Log4j

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. 先导入Log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. log4j.properties
#将等级为DEBUG的日志信息输出到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/kuang.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
  1. 配置log4j为日志的实现
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

分页

Limit分页

接口

List<User> getUserByLimit(Map<String,Integer> map);

实现Mapper

<select id="getUserByLimit" parameterType="map" resultType="User">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

测试

@Test
public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);
    map.put("pageSize",2);
    List<User> userList = mapper.getUserByLimit(map);
    for (User user : userList) {
        System.out.println(user);
    }
    sqlSession.close();
}

使用注解开发

注解只能解决一些简单的语句,对于Mybatis来说用xml更好

  1. 注解在接口上实现
@Select("select * from user")
List<User> getUsers();
  1. 需要在核心配置文件中绑定接口
<mappers>
    <mapper class="com.zaughter.dao.UserMapper"/>
</mappers>
  1. 测试

本质:反射机制实现

底层:动态代理

CRUD

注意:一定要将接口注册绑定到我们的核心配置文件中

我们可以在工具类创建的时候实现自动提交事务

public static SqlSession getSqlSession(){
    //openSesson中的true代表自动提交事务
    return sqlSessionFactory.openSession(true);
}

查找的接口

//方法存在多个参数时,所有参数前面必须加上@Param注解
@Select("select * from user where id = #{id}")//提取@Param中的内容
User getUserById(@Param("id") int id2);//以注解中的为主,参数名可以变化

添加的接口

@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);

由于工具类里设置了自动提交事务,所以测试的时候不用写提交事务的代码了

关于@Param()注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型,可以忽略,但是建议加上
  • 在SQL中引用的就是注解中设定的属性
    • 额外补充:一般SQL中都使用#{},可以防止SQL注入。${}作用差点,一边不用

Lombok

巨偷懒的注解插件

多对一association

正常SQL联表查询:select s.id,s.name,t.name from student s,teacher t where s.tid=t.id;

按照查询嵌套处理(子查询)

StudentMapper.xml

<mapper namespace="com.zaughter.dao.StudentMapper">
    <!--对应的方式是getStudent,由于要输出的类型既包含Student又包含Teacher所以用结果集-->
    <select id="getStudent" resultMap="StudentTeacher">
        select * from mybatis.student
    </select>
    
    <!--结果集-->
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--复杂的属性需要单独处理
            对象:association
            集合:collection
        -->
        <!--实体类中的属性为teacher对象,对应的student数据库字段是tid(子查询的字段)
			这里用javaType给teacher对象一个Teacher类型
		-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    
    <!--对应结果集中的getTeacher方法,相当于在结果集中内嵌一个select-->
    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher where id=#{id}
    </select>
</mapper>

Student实体类属性

private int id;
private String name;
private Teacher teacher;//关键

按照结果嵌套处理(联表查询)

StudentMapper.xml

<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from mybatis.student s,mybatis.teacher t
    where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
        <result property="id" column="tid"/>
    </association>
</resultMap>

一对多collection

Teacher实体类属性

private int id;
private String name;
private List<Student> students;

按照查询嵌套处理

<select id="getTeacher2" resultMap="TeacherStudent2">
    select * from mybatis.teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
    <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
    select * from mybatis.student where tid=#{tid}
</select>

按照结果嵌套处理

<select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from mybatis.student s,mybatis.teacher t
    where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--由于此时实体类中的students属性是一个集合,所以用collection-->
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>
  • JavaType用来指定实体类中属性的类型
  • ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

动态SQL

动态SQL可以根据不同条件生成不同的SQL语句

环境搭建

  1. 导包
  2. 编写配置文件
  3. 编写实体类
public class Blog {
    private int id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
 	//构造函数,getter,setter,toString   
}

if

接口

List<Blog> queryBlogIF(Map map);

xml中运用if

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1=1
    <!--上面的1=1是为了引出where。如果test中的内容成立(传入的Map参数中,键为title的值不为空),则追加if标签中的内容-->
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>

测试,此处单走title!=null的if语句

public void queryBlogIF(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap hashMap = new HashMap();
    hashMap.put("title","Java");
    List<Blog> blogs = mapper.queryBlogIF(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

choose(when,otherwise)

MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。otherwise用来防止空参。如果传入多个参数,choose只会执行一个,优先级从上至下

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="title!=null">
                title=#{title}
            </when>
            <when test="author!=null">
                and author=#{author}
            </when>
            <otherwise>
                and views={views}
            </otherwise>
        </choose>
    </where>
</select>

trim(where,set)

where标签和set标签实际上都是用trim标签写的,所以可以通过trim定制他们的作用

where 针对and

if中的代码我们用了where 1=1来引出where,其实这样不正式

我们可以用where标签。where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

代码变为

select * from mybatis.blog
<where>
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</where>

set 针对逗号

用于动态更新语句的类似解决方案叫做 set,会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

sql include

可以实现代码的复用

以上面where代码为例,可以等价为

<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="" .......>
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

注意事项:

  • 最好基于单表来定义SQL片段(不要太复杂,不然复用性低)
  • 不要存在where,set标签

Foreach

Test

public void queryBlogIF(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap hashMap = new HashMap();
    ArrayList<Integer> ids = new ArrayList<Integer>();
	//在集合中传入两个数
    ids.add(1);
    ids.add(2);
    hashMap.put("ids",ids);//集合传入到map中,键名为ids
    //方法传递一个map,而map中可以存在一个集合
    List<Blog> blogs = mapper.queryBlogForeach(hashMap);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

xml

<select id="" .......>
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="(" close=")" separator="or">
        	id=#{id}
        </foreach>
    </where>
</select>
  • collection为传入的集合的键名,item为从集合中遍历出来的元素的名
  • open,close,separator都是用来拼接的
  • 标签内的为拼接内容

foreach最终结果

select * from mybatis.blog where (id=1 or id=2)

where由where标签产生

(id=1 or id=2)是foreach的结果

缓存

由于连接数据库消耗资源,所以再查询第一次后可以把内容放在内存中,下次查询相同数据可以直接走缓存不走数据库

可用的清除策略有:

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

默认的清除策略是 LRU。

一级缓存 (默认开启)

  • 一级缓存也叫本地缓存:SqlSession

    • 与数据库同一次会话期间查询到的数据会放到本地缓存中
  • 缓存失效的情况:

    • 映射语句文件中的所有 select 语句的结果将会被缓存。
    • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
    • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
    • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
    • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
    • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
    • 主动使用sqlSession.clearCache()清除缓存
  • 一级缓存默认开启,但只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段

二级缓存

  • 也叫全局缓存
  • 基于namespace级别的缓存,一个命名空间对应一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放到当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;开启二级缓存后,会把一级缓存的内容保存到二级缓存中
    • 新的会话查询信息就会从二级缓存中获取内容
    • 不同的mapper查处的数据会放到自己对应的缓存中

步骤:

  1. 开启全局缓存
<settings>
    <!--显示的开启全局缓存-->
	<setting name="cacheEnabled" value="true"/>
</settings>
  1. 在当前mapper.xml中使用二级缓存<cache/>

自定义:

<cache
  eviction="FIFO"		<!--使用FIFO输出策略-->
  flushInterval="60000"		<!--每隔60秒自动刷新缓存-->
  size="512"		<!--最多保存512个缓存-->
  readOnly="true"/>		<!--只读-->

问题:

  1. 我们需要将实体类序列化,否则会报错:让实体类implements Serializable

自定义缓存ehcache

第三方缓存jar包

标签:缓存,log4j,mybatis,sqlSession,Mybatis,where,id
From: https://www.cnblogs.com/zaughtercode/p/17062952.html

相关文章

  • Mybatis
    持久层框架持久化:持久化就是将程序的数据再持久状态和瞬时状态持久化方法:数据库(jdbc)、io文件持久化内存:断电即失持久层:完成持久层的代码Dao层,Service层,Cont......
  • 【项目实战】从零到一搭建Spring Boot整合Mybatis-plus
    前言2023年想搭建一套属于自己的框架,做一个属于自己想法的项目。这些年工作中一直用公司已有的框架,以前有跟着学习视频搭建过,但自己真正动手搭建时发现问题还是很多,比如没......
  • MyBatis官方文档——XML映射文件部分
    文章目录​​XML映射器​​​​1、select​​​​2、insert,update和delete​​​​3、sql片段​​​​4、参数​​​​4.1、字符串替换​​​​5、结果映射(重难点)​​......
  • MyBatis使用foreach批量插入一个含List<实体>成员变量的实体类
    文章目录​​一、List<String>​​​​二、List<IntEntity>​​​​三、再次修改​​MyBatis使用foreach批量插入一个实体类数据,其中这个实体类包含一个List的成员变量。即......
  • MyBatis
    1、简介1.1、什么是MyBatisMyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBati......
  • Mybatis-Plus批量插入应该怎么用
    没有绝路,人也不会死在绝路上,大众只会迷失于路口文章目录​​1.准备测试环境​​​​2.saveBatch​​​​2.1分析​​​​3.insert循环插入​​​​4.自定义sql插入​​......
  • MyBatis-Plus联表查询(Mybatis-Plus-Join)
    ​​mybatis-plus​​​作为​​mybatis​​​的增强工具,简化了开发中的数据库操作。一旦遇到leftjoin或rightjoin的左右连接,还是得老老实实的打开xml文件,手写上一大段的s......
  • 分页依赖里面底层有mybatis-start 这个依赖,版本和这个项目的common的mybatis-start
    我重新提交了解决了 是我弄了分页依赖进去然后分页依赖里面底层有mybatis-start这个依赖,版本和这个项目的common的mybatis-start 版本依赖不一致导致的,我用mybatis......
  • (狂神)MyBatis-Plus
    Ref:https://www.kuangstudy.com/1、MyBatisPlus概述需要的基础:把我的MyBatis、Spring、SpringMVC就可以学习这个了!为什么要学习它呢?MyBatisPlus可以节省我们大量工......
  • 使用MyBatis-Plus报错:Invalid bound statement (not found):无法使用selectById云云
    详见MP的gitee源码Issue:https://gitee.com/baomidou/mybatis-plus/issues/I6AZIOMP版本3.5.2最近遇到了Invalidboundstatement(notfound)这个报错,网上都是说xml和mapp......