Mybatis3

MyBatis

一个封装好的增强JDBC,是一个轻量级框架,功能简单易学,SQL于java编码分离:SQL是开发人员控制。

通过配置文件和SqlSessionFactory这个重量级资源对SQL进行处理编译。

只需要掌握好sql语句,将sql语句配置在配置文件中即可

MyBatis做到了Sql和代码的分离,提高了代码的可维护性,降低了耦合,简化开发

所有的框架目的都是为了对项目的解耦合,和简化开发。

优点:

  • 简单易学
  • 灵活
  • Sql和代码分离,提高可维护性
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql

MyBatis的下载

Https://github.com/mybatis/mybatis-3/

mybatis原名是iBatis是apache基金会的一个开源项目,2010年由apache software foundation迁移到google code,并改名为mybatis,2013年迁移至github

1. 第一个mybatis程序

  1. pom.xml文件添加依赖

    <!--mybatis依赖--> 
    <dependency>
    	<groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <!--mysql依赖-->
    <dependency>
    	<groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.11</version>
    </dependency>
    
  2. 定义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">
              <!--mysql驱动-->
            <property name="driver" value="${driver}"/>
              <!--mysql链接路径 mysql8.0后需要添加serverTimezone=Asia/Shanghai或者UTC设置编码-->
            <property name="url" value="${url}"/>
              <!--数据库用户名-->
            <property name="username" value="${username}"/>
              <!--数据库密码-->
            <property name="password" value="${password}"/>
          </dataSource>
        </environment>
      </environments>
      <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
      </mappers>
    </configuration>
    

    environments:末尾是s代表可以添加多个environment子标签environments中的default绑定使用的数据库

  3. 添加实体类,实体类数据建议和数据库列一致

    public class Student{
        private int id;
        private String name;
        private int age;
        public Integer getId() { return id; }
        public void setId(Integer id) { this.id = id; }
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public int getAge() { return age; }
        public void setAge(int age) { this.age = age; }
        @Override
        public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age='" + age + '\'' + '}'; }
        public Student(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public Student() {
        }
    }
    
    
  4. 添加mybatis的接口对象,接口中定义类一个类对应一个mapper中的子标签

    public interface SqlMethods {
        List<Student> selectStudent();
        Student selectStudent1(int id);
        List<Student> selectStudent2(String name);
        int insertStudent(Student student);
        int updateStudent(Map<String,Object> map);
        int deleteStudent(String name);
    }
    
  5. 定义mapper.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="org.study.mybatis.dao.SqlMethods">
        <!--查询-->
        <select id="selectStudent" resultType="org.study.mybatis.dao.Student">
            select id,name,age from t_student
        </select>
    </mapper>
    

    namespace:绑定接口类

    id:值对应接口的方法,一个标签对应一个方法

    resultType:设置标签返回值类型,对应接口类中的方法返回值

    parameterType:参数类型,对应接口类中的方法参数

  6. 编写mybatis工具类

    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    

    这是三条固定的语句,同时因为SqlSessionFactory是mybatis中的重量级资源,耗费较大内存,因此,可以将固定的语句提取出来整合成一个工具类,需要时直接调用

    //mybatis工具类
    public class MybatisUtil {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            String resource = "Mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    

    注意,SqlSessionFactory是一个重量级资源,一个应用程序只允许出现一次,因此将其定义为static让其跟随应用程序的生成而生成,一直保持存在状态

    定义getSqlSession方法是为了方便主类的调用。

  7. 定义主类

    主类实现对数据的增删改查操作

    SqlSession sqlSession = MybatisUtil.getSqlSession();
    

    调用mybatis工具类返回一个SqlSession

select

//第一种方法
SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
List<Student> students = mapper.selectStudent();
for (Student student : students) {           			  System.out.println("id="+student.getId()+"\nname="+student.getName()+"\nage="+student.getAge());
}
//第二种方法
List<Student> list = new ArrayList<>();
List<Student> students = sqlSession.selectList("org.study.mybatis.dao.SqlMethods.selectStudent", list);
for (Student student : students) {
	System.out.println(student);
}
<!--查询-->
<select id="selectStudent" resultType="org.study.mybatis.dao.Student">
	select id,name,age from t_student
</select>

mybatis中提供了多种select的查询方式

insert

//第一种方式
Student student = new Student();
student.setId(3);
student.setName("秦先生");
student.setAge(24);
String sqlId="org.study.mybatis.dao.SqlMethods.insertStudent";
int insert = sqlSession.insert(sqlId, student);
sqlSession.commit();
System.out.println(insert);
sqlSession.close();
//第二种方式
SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
int insert = mapper.insertStudent(new Student(3, "郑先生", 21));
sqlSession.commit();
System.out.println(insert);
sqlSession.close();
<!--添加-->
<insert id="insertStudent" parameterType="org.study.mybatis.dao.Student">
	insert into t_student(id,name,age) values(#{id},#{name},#{age})
</insert>

update

//第一种方式
Student student1 = new Student();
student1.setId(3);
student1.setName("秦先生");
Map<String,Object> map = new HashMap<>(2);
map.put("name","秦先生");
map.put("id",3);
int update = sqlSession.update("org.study.mybatis.dao.SqlMethods.updateStudent", map);
System.out.println(update);
sqlSession.commit();
sqlSession.close();
//第二种方式
SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
Map<String,Object> map = new HashMap<>();
map.put("name","李四");
map.put("id",2);
int i = mapper.updateStudent(map);
sqlSession.commit();
sqlSession.close();
System.out.println(i);
<!--修改-->
<update id="updateStudent" parameterType="map">
	update t_student set name=#{name} where id=#{id}
</update>

delete

//第一种方式
SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
int i = mapper.deleteStudent("李四");
sqlSession.commit();
System.out.println(i);
sqlSession.close();
//第二种方式
int delete = sqlSession.delete("org.study.mybatis.dao.SqlMethods.deleteStudent", "张三");
System.out.println(delete);
sqlSession.commit();
sqlSession.close();
 ```xml
delete from t_student where name=#{name} ```

insert,update,delete这几个都得记得commit和close,不然数据不会提交,mybatis默认是手动提交的。

mybatis中的map集合是个很重要的东西,在mybatis中,可以说是万能的,map会感觉你相匹配的需要植入的sql语句的#{???}部分,map是采用键值对的方式Map<String,int> 就像这样,然后使用map中的put方法添加后,在方法执行时会根据键自动匹配相应的#{???},将值注入进去相应的sql语句中相应的位置

同时,参数类型paramentType="map"参数类型要改成map

实体类或数据库表,字段或者参数过多的时候,可以考虑使用map或者注解

Map传递参数,直接在sql中取出key即可!

对象传递参数,直接在sQl中取对象的属性即可!

只有一个基本类型参数的情况下,可以直接在sq中取到!

MyBatis中的模糊查询

select id="selectLike" parameterType="string" resultType="org.study.mybatis.dao.Student">
	select * from t_student where name like #{name}
</select>
SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
List<Student> student = mapper.selectLike("%林%");
for (Student student1 : student) {
System.out.println(student);
}

mapper文件中定义模糊查询的sql语句,将模糊查询的数据替换成#{???}(包含通配符% %)

然后再调用相应的方法中给予模糊查询的参数(包含通配符% %)

也可以再sql拼接中使用通配符

select * from t_student where name like "%"#{name}"%"

2. 配置文件优化

1. 核心配置文件

  • mybatis-config.xml
  • MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息
  • 下面图片显示的是mybatis-config.xml中可配置的顶层标签:

在这里插入图片描述

configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

2. 环境配置(environments)

MyBatis 可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

每个数据库对应一个 SqlSessionFactory 实例

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。

如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

详见mybatis3官网内容,数据较多copy麻烦

https://mybatis.org/mybatis-3/zh/configuration.html#environments

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

3. 属性(properties)

我们可以通过properties属性来实现引用配置文件

定义一个db.properties配置文件,保存数据库连接

mysql.driver=com.mysql.cj.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/student?serverTimezone=UTC&useSSL=false
mysql.username=root
mysql.password=123456

在mybatis主配置文件mybatis-config.xml中的configuration标签添加子标签

<properties resource="db.properties">
<!--除定义db.properties配置文件也可以直接在properties标签内写-->
	<property name="username" value="dev_user"/>
 	<property name="password" value="F2Fa3!33TYyg"/>
</properties>

两种方式同时定义的话,优先读取properties元素体内指定的信息,然后根据定义的resource属性读取类路径下的配置文件,覆盖内部的配置信息

引入db.properties配置文件,修改原先的配置文件中关于数据库连接的配置为

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver}"/>
                <property name="url" value="${mysql.url}"/>
                <property name="username" value="${mysql.username}"/>
                <property name="password" value="${mysql.password}"/>
            </dataSource>
        </environment>
    </environments>

${???}中的?对应db.properties配置文件中相应的名称

记住,xml中标签的定义是有顺序的,如下图:

在这里插入图片描述

4. 类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

当你使用typeAliases定义了别名后,可以在mybatis-config.xml主配置文件中定义,然后可以在mapper.xml文件中使用你定义的别名

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

当你指定一个包名时,会自动在包下搜索需要的类。

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

@Alias("author")
public class Author {
    ...
}
常见别名

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

5. 设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled是否允许单个语句返回多结果集(需要数据库驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成使用的默认脚本语言。一个类型别名或全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建可延迟加载对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)一个类型别名或完全限定类名。未设置
shrinkWhitespacesInSql从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)true | falsefalse
defaultSqlProviderTypeSpecifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.A type alias or fully qualified class nameNot set

一个配置完整的 settings 元素的示例如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

其中最为重要的是:

  • cacheEnabled,是否开启缓存

  • lazyLoadingEnabled:是否开启懒加载模式

  • mapUnderscoreToCamelCase:是否开启驼峰命名规则,及将数据库列名A_COLUMN转为aColumn

  • useGeneratedKeys:是否运行JDBC支持自动生成主键

  • useCoilmnLabel:使用列标签代替列名

6. 对象工厂(objectFactory)

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。比如:

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
  public Object create(Class type) {
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }}

<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

ObjectFactory 接口很简单,它包含两个创建实例用的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的。 另外,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。

7. 插件(plugins)

plugins插件

  • mybatis-generator-core
  • mybatis-plus
  • 通用mapper

8. 映射器(mappers)

MapperRegistry:注册绑定我们的Mapper文件

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:

推荐方式

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL)有问题 -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

注意点:

  • 接口和他的Mapper配置文件必须同名
  • 接口和他的Mapper配置文件必须在同一个包下
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

注意点:

  • 接口和他的Mapper配置文件必须同名
  • 接口和他的Mapper配置文件必须在同一个包下

这些配置会告诉 MyBatis 去哪里找映射文件

mapper一般写于dao层里面,一个mapper对应一部分功能

生命周期和作用域

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

mybatis的执行流程

在这里插入图片描述

mybatis官网中给的对于SqlSessionFactoryBuilder和SqlSessionFactory以及SqlSession的解释是:

SqlSessionFactoryBuilder:

​ 它的存在就是为了创建SqlSessionFactory,一旦SqlSessionFactory被创建就不需要它了,SqlSessionFactoryBuilder可以被实例化,使用和丢弃。可以重复使用SqlSessionFactoryBuilder来创建SqlSessionFactory,但是最好不要保留,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory:

可以想象为一个数据库连接池:

​ SqlSessionFactory应该在程序运行期间一直存在,不要去创建另一个实例,多次创建是一种代码的“坏习惯”。意思就是这个SqlSessionFactory是一个重量级资源,创建会消耗大量的资源,在高并发的情况下很容易造成程序的崩溃,因此我们一个程序创建一个SqlSessionFactory实例即可。

因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式

我们需要创建一个类,将SqlSessionFactory定义为一个静态代码块,提供一个方法返回SqlSession即可,当我们需要用到SqlSessionFactory的时候我们只需要调用这个类的方法返回一个SqlSession就可以继续使用

SqlSession:

连接到连接池的一个请求

​ 每个线程都应该有一个SqlSession实例。因为SqlSession是线程不安全的,使用SqlSession是不共享的。使用我们只需要在需要的时候再创建一个SqlSession,当我们获取到返回响应时,我们就可以将SqlSession关闭了。关闭SqlSession这个步骤很重要。

ResultMap

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

解决方法:

  • 起别名

    <select id="" resultType="org.xx.User"/>
    	select 
    </select>
    

ResultMap

结果集映射

实体类:id name pwd
数据库列:id name password
<!--结果集映射-->
<resultMap id="UserMap" type="User">
	<!--id与select标签中的resultMap的值保持一致,type为要映射的实体类-->
    <result column="password" property="pwd"/>
    <!--column为数据库中的字段名 property是实体类中的属性名-->
</resultMap>

<select id="getUserById" resultMap="UserMap">
	select * from t_User where id=#{id}
</select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。

  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

  • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

  • 如果世界总是这么简单就好了

mybatis中的ResultMap元素概念图

resultMap 元素有很多子元素和一个值得深入探讨的结构。 下面是resultMap 元素的概念视图。

结果映射(resultMap)
  • constructor -用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association– 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection– 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator– 使用结果值来决定使用哪个resultMap
    • case– 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
属性描述
id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。
autoMapping如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

来自mybatis官网的入土套餐

MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。

<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>

你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 我们先来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 不用紧张,我们会一步一步地来说明。虽然它看起来令人望而生畏,但其实非常简单。

<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

3. 日志

Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。

帮助开发人员排错

在mybatis-config.xml中settings标签中配置

在这里插入图片描述

mybatis中使用STDOUT_LOGGING,如果要使用另外的需要在pom.xml中配置

可以去maven仓库中查找相应的插件引入

log4j

  • Log4j是Apache的一个开源项目
  • 我们可以控制日志信息输送的目的地
  • 我们也可以控制每一条日志的输出格式
  • 定义每一条日志信息的级别
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

https://mvnrepository.com/

在这里插入图片描述
在这里插入图片描述

将箭头setting指向的配置复制进maven的pom.xml中

创建一个log4j.properties文件,对log4j进行配置

#配置根
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 = logs/log.log
log4j.appender.file.MaxFileSize = 100mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yyyy-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.xml中配置setting

<settings>
	<setting name="logImpl" value="LOG4J"/>
</settings>
简单使用
  1. 在要使用log4j的类中导入包org.Apache.log4j.Logger;

  2. 日志对象,参数为当前类的class

    public class xxx{
    	static Logger logger = Logger.getLogger(xxx.class);
        public static void main(String[] args){
            logger.info("info:进入了main");
            logger.debug("debug:进入了main");
            logger.error("error:进入了main");
        }
    }
    

4. 分页

使用分页目的

  • 减少数据的处理量

使用Limit分页

语法:

SELECT * from t_user limit startIndex,pageSize

如果一页显示十行数据的话

第一页:从0开始,显示十行数据

select * from t_user limit 0,10

第二页,从10开始,显示10行数据

select * from t_user limit 10,10

公式:(页数-1)*显示行数

页数从1开始算起

UserMapper中定义接口:

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

Mapper.xml

<select id="getUserByLimit" parameterType="map" resultType="User">
	select * from t_user limit #{startIndex},#{pageSize}
</select>
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserMapper.class);
Map<String,Integer> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize,10");
List<User> user = userDao.getUserByLimit(map);
for(User user1 : user){
    System.out.println(user1);
}
sqlSession.close();

RowBounds分页

不建议开发中使用,了解

不再使用SQL实现分页

  1. 接口

    //分页
    List<User> getUserByRowBounds();
    
  2. mapper.xml

    <select id="getUserByRowBounds" resultMap="UserMap">
    	select * from t_user
    </select>
    
  3. 测试

    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //RowBounds实现
    RowBounds rowBounds = new RowBounds(1,2);
    //通过java代码层面实现分页
    List<User> userList = SqlSession.selectList("com.mybatis.dao.UserMapper.getUserByBounds",null,rowBounds);
    for(User user :userList){
        System.out.println(user);
    }
    sqlSession.close();
    

MyBatis分页插件

https://pagehelper.github.io/

官网文档描述的很清楚

5. 注解开发

1. 面向接口编程

  • 根本原因:解耦,可扩展,提高复用性。
  • 在分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发更加规范化,简便化。
  • 使主要精力放到各对象之间的写作关系上,减少在各种功能对象的内部实现上花过多精力。
1、关于接口的理解
  • 接口是定义(规范,约束)与实现的分离
  • 接口本身反映了系统设计人员对系统的抽象理解
  • 接口有两类:一类是对一个个体的抽象,它可对应一个抽象体(abstract class); 另一类是对一个个体某一方面的抽象,即对应一个抽象面(interface)
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象或面向过程不是一个问题,而更多的体现则是对系统整体的架构
2、三个面向区别
  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题更多的体现就是对系统整体的架构

就像上学,课本规定了老师要讲的内容,老师可以自由扩展讲课内容丰富知识

例子:

SqlMapper

@Select("select * from t_student")
List<Student> selectStudent();

main:

SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
        List<Student> student = mapper.selectStudent();
        for (Student student1 : student) {
            System.out.println(student1);
        }

将省去在xml文件中定义select标签

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

本质:主要应用反射

底层:动态代理!
在这里插入图片描述

MyBatis的详细执行流程

在这里插入图片描述

2. 注解CRUD

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

sqlSessionFactory.openSession(true);设置事务自动提交

之前的例子中有提过,insert,update,delete这三个语法执行后是需要调用sqlSession.commit()方法的,原因就是因为mybatis默认是手动提交事务,因此才需要在sql执行完后调用commit()方法让事务提交

openSession()中将参数设置为true可以将事务提交设置为自动提交

注解参数
//select
@Select("select * from t_student where id=#{id} and name=#{name}")
List<Student> selectLike(@Param("id") int uid,@Param("name")String username);
}
@Insert("insert into t_student(id,name,age) values(#{id},#{name},#{age} )")
int insertStudent(Student student);
@Update("update t_student set age=#{age} where id=#{id}")
int updateStudent(Map<String, Integer> map);
@Delete("delete from t_student where id=#{id}")
int deleteStudent(int id);

方法存在多个参数,所以参数前面必须加上@Param(“value”)

@Param可以将列名不同的参数进行对应

System.out.println("查询");
SqlSession sqlSession = MybatisUtil.getSqlSession();
SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
List<Student> student = mapper.selectLike(3,"郑先生");
for (Student student1 : student) {
	System.out.println(student1);
}
int i = mapper.insertStudent(new Student(6, "秦先生", 23));
System.out.println(i);
Map<String, Integer> map = new HashMap<>();
map.put("age",24);
map.put("id",6);
int i1 = mapper.updateStudent(map);
System.out.println(i1);
int i2 = mapper.deleteStudent(6);
System.out.println(i2);
sqlSession.close();

注意:记得在mybatis-config.xml中绑定接口

<mappers>
	<mapper class="org.mybatis.dao.StudentMapper"/>
    <!--绑定配置文件-->
    <mapper resource="org/mybatis/dao/StudentMapper.xml"
</mappers>
关于@Param()注解
  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型,可以忽略,建议加上
  • 我们在SQL中引用的就是我们这里的@Param(“valuez”)中设定的属性
#{}和${}的区别

#{} 是预编译处理,像传进来的数据会加个" "(#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号)

** ∗ ∗ 就 是 字 符 串 替 换 。 直 接 替 换 掉 占 位 符 。 {}** 就是字符串替换。直接替换掉占位符。 方式一般用于传入数据库对象,例如传入表名.

使用 ${} 的话会导致 sql 注入。

所以为了防止 SQL 注入,能用 #{} 的不要去用 ${}

如果非要用 ${} 的话,那要注意防止 SQL 注入问题,可以手动判定传入的变量,进行过滤,一般 SQL 注入会输入很长的一条 SQL 语句

面试题:接口绑定有两种方式

1、使用注解,在接口的方法上面添加@Select@Update等注解,里面写上对应的SQL语句进行SQL语句的绑定。

2、通过映射文件xml方式进行绑定,指定xml映射文件中的namespace对应的接口的全路径名

什么时候用注解绑定?什么时候用xml绑定?

当SQL语句比较简单的时候,使用注解绑定就可以了,当SQL语句比较复杂的话,使用xml方式绑定,一般用xml方式绑定比较多

Lombok

Lombok 项目是一个 java 库,可自动插入编辑器并构建工具,将您的 java 弹出。
永远不要再写另一个获取器或等于方法,用一个注释您的类有一个功能齐全的建设者,自动化您的记录变量,等等。

使用步骤:

  1. idea安装Lombok插件

  2. 导入lombok的jar包

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
    	<groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
    
  3. 使用lombok注解实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student{
        private int id;
        private String name;
        private int age;
    }
    

复杂查询环境搭建

多对一处理

  1. 导入环境依赖

    <!--mybatis依赖-->
    <dependency>
    	<groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <!--mysql依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.11</version>
    </dependency>
    <!--log4j依赖-->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
        <!--lombok依赖-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
    
  2. 新建数据库,创建student和teacher表,将学生表中的学生关联教师id

    create table t_teacher(
     id int(5) primary key,
     name varchar(5)
     );
     insert into t_teacher(id,name) values(1,"林老师");
     create table t_student(
     id int(5) primary key,
     name varchar(20),
     tid int(5),
     foreign key(tid) references t_teacher(id)
     );
     insert into t_student(id,name,tid) values(1,"郑学生",1);
     insert into t_student(id,name,tid) values(2,"秦学生",1);
     insert into t_student(id,name,tid) values(3,"林学生",1);
     insert into t_student(id,name,tid) values(4,"司马学生",1);
     insert into t_student(id,name,tid) values(5,"刘学生",1);
    
  3. 创建学生和教师实体类

  • 教师类(TeacherMapper)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher{
private int id;
private String name;
}


- 学生类(StudentMapper)
```java
@Data
@AllargsConstructor
@AllargsConstructor
public class Student{
 private int id;
 private String name;
private Teacher teacher;
}
  1. 创建教师类和学生类的Mapper文件

    • 教师Mapper(TeacherMapper)

      <?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="org.mybatis.dao.TeacherMapper">
        <select id="selectTeacher" resultType="Teacher">
          select * from t_teacher
        </select>
      </mapper>
      
    • 学生Mapper(StudentMapper)

      <?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="org.mybatis.dao.StudentMapper">
        <select id="selectStudent" resultType="Student">
          select * from t_student where id = #{id}
        </select>
      </mapper>
      
  2. 创建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>
        <!--引入第三方配置给${??}赋值-->
      <properties resource="db.properties"/>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
            <property name="driver" value="${mysql.driver}"/>
            <property name="url" value="${mysql.url}"/>
            <property name="username" value="${mysql.username}"/>
            <property name="password" value="${mysql.password}"/>
          </dataSource>
        </environment>
      </environments>
      <mappers>
        <mapper resource="org/mybatis/dao/TeacherMapper.xml"/>
        <mapper resource="org/mybatis/dao/StudentMapper.xml"/>
      </mappers>
    </configuration>
    
  3. 创建db.properties

    mysql.driver=com.mysql.cj.jdbc.Driver
    mysql.url=jdbc:mysql://localhost:3306/student?serverTimezone=UTC&useSSL=false
    mysql.username=root
    mysql.password=123456
    
  4. 创建log4j配置文件

    #配置根
    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 = logs/log.log
    log4j.appender.file.MaxFileSize = 100mb
    log4j.appender.file.Threshold = DEBUG
    log4j.appender.file.layout = org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern = [%p][%d{yyyy-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
    
  5. 创建接口

    public interface SqlMethods {
    	Student selectStudent();
        Teacher selectTeacher();
    }
    
  6. mybatis工具类封装

    public class MybatisUtil {
    	private static SqlSessionFactory sqlSessionFactory;
    	static {
    		String resource = "Mybatis-config.xml";
        	InputStream inputStream = null;
        	try {
        		inputStream = Resources.getResourceAsStream(resource);
            	sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    	public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession(true);
        }
    }
    
  7. 测试

    SqlSession sqlSession = MybatisUtil.getSqlSession();
    SqlMethods mapper = sqlSession.getMapper(SqlMethods.class);
    Student student = mapper.selectStudent();
    Teacher teacher = mapper.selectTeacher();
    for(Student student1 : student){
        System.out.println(student1);
    }
    for(Teacher teacher1 : teacher){
        System.out.println(teacher1);
    }
    mapper.close();
    
  8. 查询出的结果

Student(id=1, name=郑学生, teacher=null)
Student(id=2, name=秦学生, teacher=null)
Student(id=3, name=林学生, teacher=null)
Student(id=4, name=司马学生, teacher=null)
Student(id=5, name=刘学生, teacher=null)

sql中,我们的语句是

select s.id,s.name,t.name from t_student s,t_teacher t where s.tid=t.id

mybaits中,我们之前学习的方法已经不够用了,而mybatis中给我们提供了专门处理复杂查询的方法

association:处理对象的 javaType

collection:处理集合的 ofType

第一种写法:

<select id="selectStudent" resultType="org.study.mybatis.dao.Student">
        select s.id,s.name,t.name from t_student s,t_teacher t where s.tid=t.id
    </select>
    <select id="selectStudent" resultMap="StudentTeacher">
        select s.id sid,s.name sname,t.name tname from t_student s,t_teacher t where s.tid=t.id
    </select>
    <resultMap id="StudentTeacher" type="org.study.mybatis.dao.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--复杂属性需要单独列出处理-->
        <!--处理对象-->
        <association property="teacher" javaType="org.study.mybatis.dao.Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

第二种写法:

思路:

​ 我们要查询的是两个表联合起来查询的,所以我们在两个表中都有数据

​ 因此我们可以先各自将各自要查询的数据查询出来

​ 然后通过mybatis提供的resultMap标签将两张表联合起来

<select id="selectStudent" resultMap="StudentTeacher">
        select * from t_student
    </select>
    <resultMap id="StudentTeacher" type="org.study.mybatis.dao.Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" javaType="org.study.mybatis.dao.Teacher" column="tid" select="selectTeacher"/>
    </resultMap>
    <select id="selectTeacher" resultType="org.study.mybatis.dao.Teacher">
        select * from t_teacher where id=#{id}
    </select>

以上是属于多对一的处理

回顾mysql的多对一查询方式:

  • 子查询

    select id,name FROM t_student where tid=(select id from t_teacher where name="林老师");
    

    根据老师姓名查询该名老师的学生有哪些

  • 联表查询

    SELECT table1.*, 
           table2.* 
    FROM   table1, 
           table2; 
    # 等同于内连接查询
    SELECT table1.*, 
           table2.* 
    FROM   table1 
           INNER JOIN table2; 
    

    左连接:left join: 左外连接(左连接), 以左表为主表

    左表不管能不能匹配上条件,最终都会保留:能匹配,正确的保留; 若不能匹配,右表的字段都置NULL。

    基本语法:from 左表 left join 右表 on 左表.字段 = 右表.字段;

    SELECT s.id, s.name, t.name teacherName FROM t_student s LEFT JOIN t_teacher t ON s.tid=t.id;
    

    右连接:right join: 右外连接(右连接), 以右表为主表

    右表不管能不能匹配上条件,最终都会保留:能匹配,正确的保留; 若不能匹配,左表的字段都置NULL。

    基本语法: from 左表 right join 右表 on 左表.字段 = 右表.字段;

    SELECT s.id, s.name, t.name teacherName FROM t_student s RIGHT JOIN t_teacher t ON s.tid=t.id;
    

    完全外连接:

    SELECT s.id, s.name, t.name teacherName FROM t_student s FULL OUTER JOIN t_teacher t ON s.tid=t.id;
    

一对一处理

一个老师对多个学生,对于老师来说这就是一对多

其余配置如常;变化在于两个实体类

  • student实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{
    private int id;
    private String name;
    private int tid;
}
  • teacher实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
    //一个学生拥有多个老师
    private List<Student> student;
}

  • 接口

    Teacher getTeacher(@Param("id")int id);
    
  • TeacherMapper.xml

    <!--这是一种方法 按照结果嵌套处理-->
    <select id="getTeacher" resultMap="TeacherStudent">
            select t.id tid,t.`name` tname,s.`name` sname,s.id sid,s.tid stid from t_teacher t, t_student s where t.id=s.tid and t.id=#{id};
        </select>
        <resultMap id="TeacherStudent" type="org.study.mybatis.dao.Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
            <collection property="student" ofType="org.study.mybatis.dao.Student">
                <result property="id" column="sid"/>
                <result property="name" column="sname"/>
                <result property="tid" column="stid"/>
            </collection>
        </resultMap>
    
    
    

    思路:

    1. 先将查询出想要查询的老师查询的sql写出来

    2. 然后再将通过老师id查询该名老师有哪些学生的sql写出来

    3. 通过resultMap将两条select标签串合起来,所以resultMap标签的type最终是返回给teacher实体类的,所以type要写teacher

      collection:是处理集合的

      这里使用collection是将查询student的数据存放进去一个集合这个集合会赋值给实体类中的student

      select连接下方查询student的标签 column是老师的id也是下面select的参数

    <!--这是另一种方法,按照查询嵌套处理-->
    <select id="getTeacher" resultMap="TeacherStudent">
            select id,name from t_teacher where id=#{id}
        </select>
        <resultMap id="TeacherStudent" type="org.study.mybatis.dao.Teacher">
            <collection property="student" ofType="org.study.mybatis.dao.Student" javaType="ArrayList" select="getStudentById" column="id"/>
        </resultMap>
        <select id="getStudentById" resultType="org.study.mybatis.dao.Student">
            select * from t_student where tid=#{id}
        </select>
    

小结

在上面两个例子中我们看到

关联-association 是多对一使用的

集合-collection是一对多使用的

javaType & ofType

  1. javaType 用来指定实体类中属性的类型
  2. ofType 用来指定映射到List或者集合中的泛型约束类型

尽量保证SQL的可读性保持通俗易懂

注意一对多和多对一中,属性名和字段的问题

如果问题不好排查,可以使用日志

尽量少使用Lombok,可读性不高,而且是不太好的

面试高频
  • MySql引擎

  • InnoDB底层原理

  • 索引

  • 索引优化

动态SQL

动态SQL是可以根据需要的不同自动拼接不同的SQL,是MyBatis的一个强大特性,可以简化开发

在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

if
choose (when, otherwise)
trim (where, set)
foreach

搭建环境

  1. 创建数据库

    create database Blog;
    use Blog;
    create table t_blog(
    	id VARCHAR(50) not null comment "博客id",
    	title varchar(100) not null comment "博客标题",
    	author varchar(30) not null  comment "博客作者",
    	create_time datetime not null comment "创建时间",
    	views int(30) not null comment "浏览量" 
    )engine=INNODB default CHARSET=UTF8;
    
  2. 创建基础工程

    1. 导包

      <dependency>
      	<groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.2</version>
      </dependency>
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.11</version>
      </dependency>
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.12</version>
          <scope>provided</scope>
      </dependency>
      
    2. 编写配置文件

      <?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"/>
          <settings>
              <setting name="logImpl" value="STDOUT_LOGGING"/>
              <setting name="mapUndersoreToCamelCase" value="true"/>
              <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
          </settings>
          <environments default="development">
              <environment id="development">
                  <transactionManager type="JDBC"/>
                  <dataSource type="POOLED">
                      <property name="driver" value="${mysql.driver}"/>
                      <property name="url" value="${mysql.url}"/>
                      <property name="username" value="${mysql.username}"/>
                      <property name="password" value="${mysql.password}"/>
                  </dataSource>
              </environment>
          </environments>
          <mappers>
              <mapper resource="org/study/mybatis/dao/BlogMapper.xml"/>
          </mappers>
      </configuration>
      
      mysql.driver=com.mysql.cj.jdbc.Driver
      mysql.url=jdbc:mysql://localhost:3306/Blog?serverTimezone=UTC&useSSL=false
      mysql.username=root
      mysql.password=123456
      
    3. 编写实体类

      @Data
      public class Blog{
          private int id;
          private String title;
          private String author;
          private Date ctreateTime;
          private int views;
      }
      
    4. 编写实体类对于Mapper接口和Mapper.xml文件

      BolgMapper.java

      public interface BolgMapper{
          int addBlog(Bolg blog);
      }
      

      BolgMapper.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="org.mybatis.dao.BlogMapper">
        <insert id="addBlog">
            insert into t_blog(id,title,author,create_time,views) values(#{id},#{title},#{author},#{create_time},#{views});
        </insert>
      </mapper>
      
    5. 工具类

      IDUtils,随机生成Blog的id值,replaceAll将生成的-去掉

      public class IDUtils{
          public static String getId(){
              return UUID.randomUUID().toString().replaceAll("-","")
          }
      }
      

      MybatisUtils

      public class MybatisUtil {
          private static SqlSessionFactory sqlSessionFactory;
          static {
              String resource = "Mybatis-config.xml";
              InputStream inputStream = null;
              try {
                  inputStream = Resources.getResourceAsStream(resource);
                  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          public static SqlSession getSqlSession(){
              return sqlSessionFactory.openSession(true);
          }
      }
      
    6. 测试

      SqlSession session = mybatisUtils.getSqlSession();
      BolgMapper mapper = session.getMapper(BolgMapper.class);
      Blog blog = new Blog();
      blog.setId(IDUtil.getId());
      blog.setTitle("StudyMyBatis");
      blog.setAuthor("MR.Zheng");
      blog.setCreateTime(new Date());
      blog.setViews(9999);
      int i = mapper.addBlog(blog);
      session.close();
      

IF

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM t_blog
  WHERE author = 'MR.Zheng'
  <if test="title != null"><!--test输入条件-->
    AND title like #{title}<!--满足条件时增加的部分-->
  </if>
</select>

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有author= “MR.Zheng” 的数据都会被查找出来

如果传入了title,那么被if包含的语句会拼接前面的语句

select * from t_blog where author = 'MR.Zheng and title like #{title}'

两个参数进行可选搜索该怎么办呢?

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM t_blog WHERE author = 'MR.Zheng'
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="create_time != null">
    AND create_time like #{createTime}
  </if>
</select>

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

就是传入哪个参数就由mybatis找到对应的参数标签查询

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM t_blog WHERE author = 'MR.Zheng'
  <choose>
    <!--如果传入这个就拼接这个-->
    <when test="title != null">
      AND title like #{title}
    </when>
    <!--如果传入的是这个就拼接这个-->
    <when test="create_time != null">
      AND create_time like #{createTime}
    </when>
    <!--如果上面两个都不符合就拼接这个-->
    <otherwise>
      or title='Spring'
    </otherwise>
  </choose>
</select>

when只会拼接一句,它的意思是如果满足则,满足条件后不会再向下执行

trim、where、set

where 标签

如果我们想连上面where后面的author条件一起设置成动态条件

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM t_blog
  WHERE
  <if test="author != null">
    state = #{author}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND create_time like #{createTime}
  </if>
</select>

如果第一个条件不满足,满足第二个条件那么拼接会变成select * from t_blog where and title like %Spring%

这样的SQL是错误的

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM t_bloh
  <where>
    <if test="author != null">
         auhtor = #{author}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="create_time != null">
        AND create_time like #{createTime}
    </if>
  </where>
</select>

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

Set

**set:用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。**比如:

<update id="updateAuthorIfNecessary">
  update t_blog
    <set>
      <if test="author != null">author=#{author},</if>
      <if test="title != null">title=#{tiele},</if>
      <if test="create_time != null">create_time=#{create_time},</if>
      <if test="views != null">views=#{views}</if>
    </set>
  where title=#{title}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

set会根据你传入的数据进行判断,如果你的数据包含其中某个条件,那么set会自动对Update进行拼接,并且会自动删除多余的逗号

update t_blog set author="MR.Zheng" where title="Spring"

trim

trim是与where和set等价的自定义元素,可以代替where和set使用

where:

​ prefix:翻译是前缀的意思,就是表示你要在前面添加上面。

​ prefixOverrides:翻译是前缀覆盖,表示要去除你包含的语句的前缀

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

set:

​ suffixOverrides:翻译就是后缀覆盖的意思,表示你要去除的后缀是什么

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>
SQL片段

有时候,我们会将公共部分抽取出来,方便重复使用

<!--定义可复用sql,sql公共部分-->
<sql id="if-title-author">
	<if test="title!=null">
    	title=#{title}
    </if>
    <if test="author!=null">
    	and author=#{author}
    </if>
</sql>
<!--引用可复用sql-->
<select id="selectBlog" parameterType="map" resultType="Blog">
	<include refid="if-title-author"/>
</select>

注意事项:

  • 最好使用单表来定义SQL片段
  • 不要存在where标签
foreach

select * from t_blog where 1=1 and (id=1 or id=2 or id=3);

<select id="queryBlogForeach" parameterType="map" resultType="blog">
	select * from t_blog
    <where>
        <!--collection存放一个键为ids的map集合,item是由collection查询出来的id,open是表示从哪里开始,close表示从哪里结束,separator表示分隔符,转换完后会将item的id赋值给#{id}-->
    	<foreach collection="ids" item="id" open="and(" close=")" separator="or">
        	id=#{id}
        </foreach>
    </where>
</select>

接口

List<Blog> queryBlogForeach(Map map);

测试

Sqlsession sqlSession = Mybatis.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
Map map = new HashMap();
map.put("ids",ids);
List<Blog> blog = mapper.queryBlogForeach(map);
fo(Blog blog1 : blog){
    System.out.println(blog1);
}
sqlSession.close();

动态SQL的本质上还是SQL,只是在SQL的基础上增添了逻辑代码

动态SQL就是在拼接SQL语句,我们只要保证SQl的正确性,按照SQL的格式去排列组合就可以了

缓存

查询:连接数据库,耗资源

一次查询的结果,给他暂存再一个可以直接取到的地方–>内存 :缓存

再次查询相同数据的时候,直接走缓存,就不用耗费资源重新从数据库中查询相关数据

  1. 什么是缓存[Cache ]?

    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?

    • 减少和数据库的交 次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?

    • 经常查询并且不经常改变的数据。【可以使用缓存】
    • 经常改变,不经常用的数据【不能使用缓存】

在这里插入图片描述

MyBatis缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。

MyBatis默认情况下,只启用了本地的会话缓存(一级缓存),它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:<cache/>

二级缓存是基于namespace级别的缓存

为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来实现自定义二级缓存

基本上就是这样。这个简单语句的效果如下:

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

提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

可用的清除策略有:

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

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

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

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

一级缓存

测试方法:

  1. 开启日志
  2. 在SqlSession中查询2次同样数据

缓存失效情况:

  1. 查询不同数据

  2. 增删改查,可能会更改原来的数据,所以缓存会进行刷新

  3. 查询不同的Mappe.xml

  4. 手动清除缓存

    sqlSession.clearCache();

默认是一级缓存,只在一次sqlSession中有用也就是拿到连接到关闭连接这个区间有用

每个用户来查都会开启一个SqlSession,会频繁操作数据库

一级缓存相当于一个Map

二级缓存

<mapper ...>
	<cache/><!--开启二级缓存-->
</mapper>
<cache
       eviction="LRU | FIFO | SOFT | WEAK"
       flushInterval="60000"
       size="1024"
       readOnly="true"
/>
<!--eviction:选择清除策略
	flushInterval:刷新间隔,毫秒
	size:最多存储要缓存的对象,结果或列表的1024个引用,默认值是1024
	readOnly:是否只读
-->

mybatis-config.xml

<settings>
	<setting name="cacheEnabled" value=""/><!--显示开启全局缓存-->
</settings>
  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中
<select id="select" resultType="user" useCache="false">
	select * from t_user
</select>

二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

使用二级缓存我们需要将实体类序列化,否则会报错

让实体类去实现Serializable接口

小结:

  • 只要开启了二级缓存,在同一个Mapper下有效
  • 所有的数据会先放在以及缓存中
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中

缓存原理

在这里插入图片描述

自定义缓存

Ehcache:

​ EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点

​ Ehcache是一种广泛使用的开源Java分布式缓存

​ 主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

使用ehcache

  1. 导包

    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.1.0</version>
    </dependency>
    
    
  2. 配置

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
  3. 创建并配置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>
    
    <!--https://www.cnblogs.com/zqyanywn/p/10861103.html-->
    

http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=“http://ehcache.org/ehcache.xsd”
updateCheck=“false”>




   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值