Mybatis detailed explanation (super comprehensive)

Table of contents:

1. Getting Started with MyBatis

1. What is MyBatis

​ MyBatis is an excellent persistence layer framework that supports custom SQL, stored procedures, and advanced mapping. MyBatis eliminates almost all JDBC code and the work of setting parameters and getting result sets. MyBatis can configure and map primitive types, interfaces, and Java POJOs (Plain Old Java Objects, Plain Old Java Objects) as records in the database through simple XML or annotations.

2、MyBatis、Hibernate 和 JDBC

  1. Compared with Hibernate, MyBatis has lower encapsulation than Hibernate, but it has excellent performance, small size, easy to learn, and wide application

  2. Compared with JDBC, MyBatis reduces the amount of code by more than 50%, and meets the requirements of high concurrency and high response

3. Advantages and disadvantages

3.1 Advantages

  • MyBatis is free and open source.
  • Compared with JDBC, the amount of code is reduced by more than 50%.
  • MyBatis is the simplest persistence framework, small and easy to learn.
  • MyBatis is quite flexible and will not impose any impact on the existing design of the application or database. SQL is written in XML, separated from the program logic code, reducing coupling, facilitating unified management and optimization, and improving code reusability.
  • Provides XML tags and supports writing dynamic SQL statements.
  • Provides mapping tags to support ORM field relationship mapping between objects and databases.
  • Stored procedures are supported. MyBatis encapsulates SQL in the form of stored procedures, which can keep business logic outside the database, enhance the portability of applications, and make them easier to deploy and test.

3.2 Disadvantages

  • Writing SQL statements requires a lot of work, which requires developers to have a solid foundation in writing SQL statements.
  • SQL statements depend on the database, resulting in poor database portability, and the database cannot be replaced at will.

3.3 Usage Scenarios

​ MyBatis focuses on SQL itself and is a flexible enough DAO layer solution. It is suitable for projects with high performance requirements and frequent changes in requirements, such as Internet projects

4. Installation

​ To use MyBatis, just put mybatis-x.x.x.jarthe file in the classpath (classpath). If you use Maven to build the project, you need to put the following dependency code in the pom.xml file

1. For example, in a Maven project, import MyBatis3.5.2 version dependencies

<!--MyBatis依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<!--数据库依赖-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

2. The first MyBatis program

1. Build SqlSessionFactory from XML

​ Every MyBatis-based application centers on an instance of SqlSessionFactory. An instance of SqlSessionFactory can be obtained by SqlSessionFactoryBuilder. The SqlSessionFactoryBuilder can build a SqlSessionFactory instance from an XML configuration file or a pre-configured Configuration instance.

1.1 XML configuration

​The configurationmybatis-config.xml file contains the core settings of the MyBatis system, including the data source (DataSource) for obtaining the database connection instance and the transaction manager (TransactionManager) that determines the transaction scope and control method. The details of the XML configuration file will be discussed later, here is a simple example:

<?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配置内容时,有顺序限制-->
<configuration>
    <!--mybatis环境,default属性指定一个环境-->
    <environments default="development">
        <!--id属性自定义的环境唯一标识符-->
        <environment id="development">
            <!--指定使用jdbc事务管理-->
            <transactionManager type="JDBC"/>
            <!--使用mybatis内部带连接池功能的数据源-->
            <dataSource type="POOLED">
                <!--获取配置驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--获取配置url-->
                <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf8"/>
                <!--获取配置账号-->
                <property name="username" value="root"/>
                <!--获取配置密码-->
                <property name="password" value="112112"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
    <mappers>
        <!--使用相对于类路径的资源引用  这是相对于类路径中-->
		<mapper resource="mapper/UserMapper.xml"/>
        <!--<mapper resource="com/hqyj/mapper/UserMapper.xml"/>-->
    </mappers>
</configuration>

1.1.1 Precautions

1. Pay attention to the declaration of the XML header, which is used to verify the correctness of the XML document

2. The mappers element contains a set of mappers (mapper), and the XML mapping files of these mappers contain SQL code and mapping definition information

3. For mappers, when <mapper resource="mapper/UserMapper.xml"/>importing XML files, either the XML files are under the resources of the maven project, or the following content needs to be imported in the pom.xml file

<build>
    <!--resources配置解决mybatis 的mapperXml配置在java路径不被扫描的问题 -->
    <resources>
        <resource>
            <directory>src/main/java</directory>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!--跳过测试 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
    </plugins>
</build>

1.2 Get SqlSessionFactory

// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);

1.3 Design database tables

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(255) NULL DEFAULT NULL,
  `state` int(255) NULL DEFAULT 1,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

INSERT INTO `user` VALUES (1, 'admin', '111', 20, 1);
INSERT INTO `user` VALUES (2, '王五', '789', 24, 1);
INSERT INTO `user` VALUES (3, '小王', '123', 88, 1);

1.4 Writing JavaBean

First, download the plugin Lombok in IDEA

Then import dependencies in pom.xml

<!--Lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
</dependency>

Finally, use annotations instead of setters, getters, toString, and constructors

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private int id;
    private String name;
    private String password;
    private int age;
    private int deptId;
    private int state;
}

1.5 Write UserMapper

<?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.hqyj.mapper.UserMapper">
    <select id="getUserList" resultType="com.hqyj.pojo.User">
        select * from `user`
    </select>
</mapper>

1.6 Testing

// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL---方式一
// SqlSession执行文件中定义的SQL,并返回映射结果,参数可以直接输入方法名
// List<User> userList = sqlSession.selectList("com.hqyj.cl.mapper.UserMapper.getUserList");
// 执行SQL---方式二
// 通过SqlSession对象获取StudentMapper的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// 使用代理mapper对象查询全部的用户信息
List<Student> studentList = mapper.selectAll();
// 输出内容
System.out.println(userList);

1.7 #{} and ${}

#{}It is safe to pass value in the method, and it acts as a placeholder, which is a parameter placeholder?, that is, sql precompilation, which can prevent sql injection

${}Replacement for strings, that is, sql splicing, cannot prevent sql injection

1.7.1 Injection case

Notice:

​ The method of using ${}will get the configuration property content in jdbc.properties, so pay attention

​ If there are more than or equal to 2 parameters in the UserMapper interface, then there are 3 ways to solve the problem that the parameters cannot be found

1. Encapsulate into a Map collection method (not to mention for now, it is more cumbersome)

2. Encapsulate into a corresponding pojo class object (the parameters of the login method are directly replaced with User objects)

3. Use the annotation @Param to specify the parameter name to adjust the mapper interface method

​ login(@Param(“username”)String username,@Param(“password”)String password)

​Official recommendation: use annotations if the number of parameters is less than 5, and use objects if the number of parameters is more than 5

List<Student> login(@Param("username")String username,@Param("password")String password);
<select id="login" resultType="com.hqyj.cl.pojo.Student">
    select * from `student` where username='${username}' and password='${password}'
</select>
public class TestStudentDao {
    
    
    public static void main(String[] args) throws IOException {
    
    
        InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        //可以调整login参数只有username以及mapper的sql 发现可以查出,但是2个参数不行
        List<Student>  student = mapper.login("admin","111222' or '1'='1");
        System.out.println(student);
    }
}

3. Core objects

MyBatis has three basic elements:

  • Core Interfaces and Classes
  • MyBatis core configuration file (mybatis-config.xml)
  • SQL mapping file (mapper.xml)

The core interfaces and classes of MyBatis are as follows

                           bulid()                        openSession()
SqlSessionFactoryBuilder  --------->  sqlSessionFactory  --------------->  sqlSession 

Every MyBatis application centers around an instance of a SqlSessionFactory object.

First obtain the SqlSessionFactoryBuilder object, which can be constructed according to the XML configuration file or the instance of the Configuration class.

Then get the SqlSessionFactory object, which can be obtained through the SqlSessionFactoryBuilder object.

With the SqlSessionFactory object, you can then get the SqlSession instance. The SqlSession object completely contains all the methods of executing SQL operations with the database as the background, and the mapped SQL statement can be executed directly with this instance.

**It should be noted that:**Each thread has its own SqlSession instance, and the SqlSession instance cannot be shared, nor is it thread-safe. Therefore, the scope of SqlSession is within the scope of request or method body.

Mention request Recall the redirection and forwarding learned in the previous stage. The redirection is not the same request, but two requests, and the request scope will not be shared.

1、SqlSessionFactoryBuilder

SqlSessionFactoryBuilder generates SqlSessionFactory based on configuration information or code, and provides multiple build() method overloads, which all call the same signature method, namely:

build(Reader reader, String environment, Properties properties)

Since the parameters environment and properties can both be null, to remove duplicate methods, there are actually only three overloaded methods as follows:

  • build(InputStream inputStream, String environment, Properties properties)
  • build(Reader reader, String environment, Properties properties)
  • build(Configuration config)

Through the above analysis, it is found that configuration information can be provided to the build() method of SqlSessionFactoryBuilder in three forms, namely InputStream (byte stream), Reader (character stream), and Configuration (class).

Since both byte stream and character stream belong to the way of reading configuration files, it is easy to think of two ways to build a SqlSessionFactory, namely: reading XML configuration files and writing code. It is generally used to construct SqlSessionFactory in the form of XML configuration files, so that on the one hand, hard coding can be avoided, and on the other hand, it is convenient for future configuration personnel to modify and avoid repeated compilation of code.

1.1 Life cycle and scope of SqlSessionFactoryBuilder

The biggest feature of SqlSessionFactoryBuilder is that it is thrown away after use. After the SqlSessionFactory object is created, this class does not exist, because the best scope of SqlSessionFactoryBuilder exists in the method body, that is, local variables.

2、SqlSessionFactory

SqlSessionFactory is a factory interface rather than a real class. His task is to create SqlSession; all MyBatis applications are centered on the SqlSessionFactory instance, and the instance of SqlSessionFactory can be obtained through the SqlSessionFactoryBuilder object

2.1 Life cycle and scope of SqlSessionFactory

Once a SqlSessionFactory object is created, it persists throughout the application. There is no reason to destroy or recreate it, and it is not recommended to create the SqlSessionFactory multiple times during an application run. Therefore, the best scope of SqlSessionFactory is Application, which exists with the life cycle of the application. This "exists during the entire application runtime, and there is only one object instance" mode is the so-called singleton mode (referring to one and only one instance during runtime).

3、SqlSession

SqlSession is an object used to perform persistent operations, similar to Connection in JDBC. It provides all the methods needed to execute SQL commands against the database, and can directly run mapped SQL statements through the SqlSession instance.

There are two main uses of SqlSession:

  1. Get a mapper. Let the mapper find the corresponding SQL through the namespace and method name, send it to the database, and return the result after execution.
  2. Execute SQL directly through "namespace (namespace) + SQL id", without obtaining a mapper.

3.1 SqlSession life cycle and scope

SqlSession corresponds to a database session. Since the database session is not permanent, the life cycle of SqlSession is not permanent, and the SqlSession object needs to be created every time the database is accessed. Encapsulation methods are recommended to reclaim resources.

4. Configuration file

In the first MyBatis program, we have written the configuration file of MyBatis mybatis-config.xml, let's talk about the content of the configuration file in detail

1. Structure of MyBatis configuration file

**Precautions: **The element nodes in the mybatis-config.xml file have a certain order, and the node positions must be sorted according to the above positions, otherwise there will be compilation errors.

<?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 /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

1.1 configuration label

The configuration element is the root node of the entire XML configuration file, and its role is equivalent to the manager of MyBatis. All configuration information of MyBatis will be stored in it.

1.2 properties tag

The properties tag can specify an external properties file (dbconfig.properties) through the resource attribute, or it can be configured through the properties sub-element. as follows

<!--驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--获取连接对象地址-->
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf8"/>
<!--数据库用户名-->
<property name="username" value="root"/>
<!--数据库密码-->
<property name="password" value="112112"/>

Reference the above variables in the environments node

<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>

Starting from version 3.4.2 of mybatis, the function of providing attribute placeholders (knowledge)
is off by default. Manually enable setting
1, and enable setting. <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
If you use the ":" character in the attribute name (such as: db:username), or in The ternary operator of the OGNL expression is used in the SQL mapping
(such as: table N ame ! = null ? table N ame : 'globalconstants'), it is necessary to set a specific attribute to modify the character separating the attribute name and the default value 2. Setting Other configuration' < propertyname = " org . apache . ibatis . parsing . Property Parser . default − value − separator " value = " ? : " / > ' 3. Modify and go back to the account property ' < propertyname = " username " value = " {tableName != null ? tableName : 'global_constants'}), you need to set specific properties to modify the characters separating the property name and the default value 2. Set other configuration `<property name="org.apache.ibatis.parsing.PropertyParser .default-value-separator" value="?:"/>` 3. Modify the account property` <property name="username" value="tab l e N am e ! _=null?tableName:globalconstants ), you need to set specific properties to modify the character separating the property name and the default value2, set other configurations'<p ro p er t y of them="org.apache.ibatis.parsing.PropertyParser.defaultvalueseparator"value="?:"/>'3. Modify and go back to account number p ro p er t y '<p ro p er t y of them="username"value=" {username?:root}"/> 4、这样,如果没有设置`, is to use root

1.3 settings tab

​ The settings tag is used to configure the runtime behavior of MyBatis. It can profoundly affect the underlying operation of MyBatis. Generally, a lot of configuration is not required, and its default value can be used in most cases. In most cases, use it to configure logs, enable automatic hump naming rule mapping, and enable second-level cache (enabled by default).

1.3.1 Enable automatic camel case naming rule mapping

<settings>
    <!-- 开启驼峰命名规则 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

1.3.2 Configuration log

<settings>
    <!-- 配置log4j日志 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>

1.3.3 Enable L2 cache

<settings>
    <!--开启二级缓存,默认是开启的,显示定义出来-->
    <setting name="cacheEnabled" value="true"/>
</settings>

1.4 typeAliases tag

In order not to specify the fully qualified name of the class everywhere, we can define an alias using the typeAliases tag. Divided into two ways:

  1. alias a class
<typeAliases>
    <!--方式一,逐个设置,大小写不受影响-->
    <typeAlias type="com.hqyj.cl.pojo.User" alias="User"/>
    <typeAlias type="com.hqyj.cl.pojo.Person" alias="Person"/>
</typeAliases>
  1. Alias ​​the contents of a folder
<typeAliases>
    <!--方式二,统一设置
    在没有注解@Alias()的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
    比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值-->
    <package name="com.hqyj.cl.pojo"/>
</typeAliases>

1.5 typeHandlers tag

​ typeHandlers mainly converts the acquired value into a Java type reasonably. In typeHandler, it is divided into jdbcType and javaType, among which jdbcType is used to define database type, and javaType is used to define Java type, and the role of typeHandler is to undertake the mutual conversion between jdbcType and javaType. We don't need to set it manually because MyBatis has default type handlers.

1.6 environments label

​ In the environments tab, multiple sets of operating environments of MyBatis can be configured to map SQL to multiple different databases. A default operating environment (specified by default) must be specified. At present, we only need to configure a set of MySQL.

1.6.1 transactionManager tag

​ MyBatis supports two transaction managers, namely JDBC and MANAGED. We choose JDBC.

1.6.2 dataSource tag

​ Used to configure the connection properties of the database, such as the driver name, URL, username and password of the database to connect to, etc. There are three values ​​(UNPOOLED, POOLED, JNDI), we choose POOLED

<!--数据库连接源-->
<dataSource type="POOLED">
    <!--获取配置驱动-->
    <property name="driver" value="${driver}"/>
    <!--获取配置url-->
    <property name="url" value="${url}"/>
    <!--获取配置账号-->
    <property name="username" value="${username!=null?username:root}"/>
    <!--获取配置密码-->
    <property name="password" value="${password}"/>
</dataSource>

1.7resultMap mapping (in the Mapper.xml file)

<!--定义结果集映射-->
<resultMap id="userMap" type="com.hqyj.pojo.User">
    <!--id主键 使用id标签-->
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
</resultMap>

1.9 mappers tag

The mappers tag is used to specify the path of the MyBatis SQL mapping file.

1.9.1 mapper label

​ mapper is a subtag of mappers. The resource attribute in mapper is used to specify the path of the SQL mapping file (class resource path). There are many ways, three of which are used here

<!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
<mappers>
    <!--使用相对于类路径的资源引用,这是相对于类路径,UserMapper.xml存在resources下的mapper包中-->
    <mapper resource="mapper/UserMapper.xml"/>

    <!-- 使用映射器接口实现类的完全限定类名 mapper文件必须和接口在同一个包下,类名和mapper文件名必须相同-->
    <mapper class="com.hqyj.cl.mapper.UserMapper"/>

    <!-- 将包内的映射器接口实现全部注册为映射器 只需要指定到包名即可,mapper文件必须和接口在同一个包下,接口名和mapper文件名必须相同-->
    <package name="com.hqyj.cl.mapper"/>
</mappers>

Five, Mapper (mapper)

​ The mapper is the most important file in MyBatis, which contains a set of SQL statements (such as query, add, delete, modify), which are called mapping statements or mapping SQL statements.

The mapper consists of a Java interface and an XML file (or annotation), and its functions are as follows:

  • Define parameter type
  • Configure the cache, the default is the first level cache
  • Provide SQL statement and dynamic SQL
  • Define the mapping relationship between query results and POJO

The mapper has the following two implementations

  • It is realized by XML file, such as the XML file we describe in the mybatis-config.xml file, which is used to generate mapper. recommended way.
  • Realized by annotation, use the Configuration object to register the Mapper interface.

1. XML implementation mapper

The XML definition mapper is divided into two parts: interface and XML.

1.1 Define the interface

public interface UserMapper {
    
    
    List<User> getUserList();
}

1.2 Define 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.hqyj.cl.mapper.UserMapper">
    <select id="getUserList" resultType="com.hqyj.cl.pojo.User">
        select * from `user`
    </select>
</mapper>

1.2.1 Description

  1. namespace is used to define a namespace, which is consistent with the fully qualified name of the defined interface.
  2. <select> The element indicates that this is a query statement, and the attribute id is used to identify this SQL. resultType indicates that what is returned is a value of type User.

1.3 Modify MyBatis configuration file

Add the following code to the MyBatis configuration file

<!--该语句用来引入 XML 文件,MyBatis 会读取 UserMapper.xml 文件,生成映射器-->
<mapper resource="com/hqyj/cl/mapper/UserMapper.xml"/>

1.4 Test code

@Test
public void getUserList() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 通过SqlSession对象获取StudentMapper的代理对象
    StudentMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 使用代理mapper对象查询全部的用户信息
    List<User> userList = mapper.getUserList();
    System.out.println(userList);
}

2. Annotation implements mapper

To implement the mapper using annotations, you only need to use Java annotations in the interface and inject SQL

2.1 Test code

public interface UserMapper {
    
    
    @Select(value = "select * from user")
    public List<User> getUserList();
}

2.1.1 Description

  1. We used the @Select annotation and injected the same select statement as in the XML.
  2. If both annotations and XML files are used to define at the same time, the XML method will override the annotation method. (Only use one method of configuration, and errors may be reported)
  3. For simple statements, you can use annotations, and for complex statements, it is recommended to use XML.

3. Main elements of MyBatis mapper

It will be explained one by one in the follow-up study

element name describe Remark
mapper The root node of the mapping file, with only one attribute named namespace The role of namespace is as follows: it is used to distinguish different mappers, and globally uniquely binds the DAO interface, that is, interface-oriented programming. When a namespace is bound to an interface, you don't need to write the implementation class of the interface. MyBatis will find the corresponding mapper configuration through the fully qualified name of the interface to execute the SQL statement. Therefore, the name of the namespace must be the same as the name of the interface.
select Query statement, one of the most commonly used and most complex elements You can customize parameters, return result sets, etc.
insert insert statement Returns an integer after execution, representing the number of inserted items
update update statement Returns an integer after execution, representing the number of updated items
delete delete statement Returns an integer after execution, representing the number of deleted items
parameterMap Define the parameter mapping relationship Element to be removed, deprecated
sql Allows to define a part of SQL and then refer to it everywhere For example, a table column name, we can define it once and use it in multiple SQL statements
resultMap Used to describe the correspondence between database result sets and objects, it is the most complex and powerful element Provide mapping rules
cache Configure the cache for a given namespace -
cache-ref References to other namespace cache configurations -

Six, MyBatis executes SQL in two ways

MyBatis has two ways to execute SQL statements, as follows:

  1. Send SQL through SqlSession
  2. Obtain the Mapper interface through SqlSession, and send SQL through the Mapper interface

1、SqlSession

After there is a mapper, you can send SQL through SqlSession. There are two common query methods in MyBatis, namely selectOne and selectList.

1.1 selectOne

The selectOne method indicates that the query is used and only one object is returned, and the query condition must be specified. Only 0 or 1 record can be queried, and if more than 1 record, the operation error will occur. Common formats are as follows (there are other overloading methods, choose according to your needs)

sqlSession.selectOne(String arg0, Object arg1)

1.2 selectList

The selectList method represents a query and returns a list. 0 or N records can be queried. Common formats are as follows

sqlSession.selectList(String arg0, Object arg1)

1.3 Description

  1. In the above syntax format, the String object is composed of a namespace and SQL id, which completely locates a SQL, so that MyBatis will find the corresponding SQL. The Object object is the parameter that needs to be passed, that is, the query condition
  2. The selectList implemented by selectOne can be implemented, that is, there is only one object in the list. But what selectList can achieve, selectOne may not be able to achieve
  3. If there is only one SQL whose id is getUserList in MyBatis, then the package path name can be removed

2. Mapper interface

SqlSession can also obtain the Mapper interface and send SQL through the Mapper interface, as shown below

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();

2.1 Description

  1. Obtain a Mapper interface through the getMapper method of SqlSession, and then call its methods. Because the SQL defined by the XML file or the interface annotation can be searched by "fully qualified name of the class + method name", MyBatis will enable the corresponding SQL to run and return the result.

Seven, CRUD label

​ Only the select tag has the resultType attribute, because only the query operation needs to specify the return result type accordingly.

1. select tag

In MyBatis, the select tag is the most commonly used and powerful SQL language tag for performing query operations. The format is as follows

<select id="queryUserByUsername" resultType="user" parameterType="string">
    select * from `user` where `username` like concat ('%',#{username},'%')
</select>

1.1 Description

  1. Parameters can be defined when executing SQL statements, and parameters can be a simple parameter type, such as int, float, String; or a complex parameter type, such as JavaBean, Map, etc. MyBatis provides powerful mapping rules. After executing SQL, MyBatis will automatically map the result set to JavaBean.
  2. In order to automatically match the query results of the database with the attributes in the return value type, the same set of naming rules is usually used for the MySQL database and JavaBean, that is, the Java naming hump rule, so that no mapping is required (database table field names and attributes Manual mapping is required when the names are inconsistent)
  3. The passing and use of parameters #{参数名}is equivalent to telling MyBatis to generate PreparedStatement parameters.

1.2 Common attributes

attribute name describe Remark
id It is used in combination with Mapper's namespace and is a unique identifier for MyBatis to call If the namespace + id is not unique, then MyBatis throws an exception
parameterType Indicates the fully qualified name or alias of the incoming parameter type of the incoming SQL statement. It is an optional attribute, and MyBatis can infer the parameters of the specific incoming statement Support basic data types and complex data types such as JavaBean and Map
resultType The type (fully qualified name or alias) returned after the SQL statement is executed. If it is a collection type, the type of the collection element is returned, and one of resultType or resultMap can be used when returning -
resultMap It is a reference to a mapping set, <resultMap>used with the element, and can be returned with one of resultType or resultMap It is the most complex element of MyBatis, which can configure mapping rules, cascade, typeHandler, etc.
flushCache Used to set whether to require MyBatis to clear the local cache and second-level cache of the previous query after calling the SQL statement The default value is false. If set to true, the local cache and the second-level cache will be cleared whenever the SQL statement is invoked
useCache Enable the switch of the second-level cache, the default value is true, which means that the query results are stored in the second-level cache -
timeout Used to set the timeout parameter, the unit is seconds (s), and an exception will be thrown when the timeout expires -
fetchSize Get the total number of records set The default value is the number set by the JDBC driver provided by the database manufacturer
statementType Tell MyBatis which JDBC Statement to work with, the value is STATEMENT (Statement), PREPARED (PreparedStatement), CALLABLE (CallableStatement) -
resultSetType This is for the ResultSet interface of JDBC, and its value can be set to FORWARD_ONLY (only allows forward access), SCROLL_SENSITIVE (two-way scrolling, but not updated in time), SCROLLJNSENSITIVE (two-way scrolling, timely update) -

1.3 Multiple parameters

​ Sometimes, we need to pass multiple parameters to execute SQL statements, such as user login; at this time, there are three ways to achieve it, namely using Map to pass parameters, using annotations to pass parameters, and using JavaBean to pass parameters; considering performance, etc. Question, it is not recommended to use Map to pass parameters, so we now analyze the latter two cases

1.3.1 Using annotations to pass parameters

Use the annotation @Param() of MyBatis to pass parameters, as shown below

1.3.1.2 Case
  1. interface
//用户登录
User login(@Param("username")String username,@Param("password")String password);
  1. Mapper
<!--用户登录-->
<select id="login" resultType="User">
    select * from user where username = #{username} and password = #{password}
</select>
1.3.1.2 Description

​ When we pass the parameter to the background, MyBatis will know that #{name} represents the name parameter through the name provided by @Param, which improves the readability of the parameter. But if this SQL has a query with 10 parameters, it will reduce readability and increase code complexity.

1.3.2 Using JavaBean to pass parameters

In the case of too many parameters, MyBatis allows to organize a JavaBean, and set parameters through simple setter and getter methods to improve readability. As follows

1.3.2.1 Case
  1. interface
//增加用户
int selectUser(User user);
  1. Mapper
<!--增加用户-->
<select id="selectUser" resultType="User">
    select * from user where username = #{username} and password = #{password} and position = #{position}
</select>

1.3.3 Differences

The difference between the above two methods is as follows.

  • Passing parameters using the @Param annotation will be affected by the number of parameters. When n≤5, it is the best way to pass parameters, because it is more intuitive; when n>5, multiple parameters will bring difficulties to the call.
  • When the number of parameters is greater than 5, it is recommended to use the JavaBean method.

2. insert tag

The MyBatis insert tag is used to define the insert statement and execute the insert operation. When MyBatis executes an insert statement, it returns the number of rows it affects the database.

2.1 Case

Add user function through insert tag

  1. interface
//增加用户
int addUser(User user);
  1. Mapper
<!--增加用户-->
<insert id="addUser">
    insert into user values (null,#{username},#{password},#{userPhone},#{createTime},#{position})
</insert>

test class

sqlSession.commit(); requires commit

2.2 Common properties

attribute name describe Remark
id It is used in combination with Mapper's namespace and is a unique identifier for MyBatis to call MyBatis throws an exception if the namespace + id is not unique
parameterType The fully qualified name or alias of the parameter type passed into the SQL statement, which is an optional attribute. Support basic data types and complex data types such as JavaBean and Map
keyProperty The function of this attribute is to assign the return value of the insert operation to an attribute of the PO class, usually the attribute corresponding to the primary key. If it is a joint primary key, multiple values ​​can be separated by commas. -
useGeneratedKe 该属性用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。 该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置执行该操作的最大时限,如果超时,就抛异常。 -
databaseId 取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 <if test="_databaseId = 'oracle'"> 来为特定数据库指定不同的 sql 语句。 MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
keyColumn 该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。 -

2.3 多参数

和select标签规则一样

3、update标签

MyBatis update 标签用于定义更新语句,执行更新操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

3.1 案例

  1. 接口
//通过用户姓名修改用户
int updateUserByUserName(User user);
  1. Mapper
<!--通过用户姓名修改用户-->
<update id="updateUserByUsername">
    update user set phone = #{phone},password = #{password} where username = #{username}
</update>

3.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 -
statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 -

3.3 多参数

和select标签规则一样

4、delete标签

MyBatis delete 标签用于定义 delete 语句,执行删除操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

4.1 案例

  1. 接口
//删除某个用户
int delUserById(int id);
  1. Mapper
<!--删除某个用户-->
<delete id="delUserById">
    delete from user where id = #{id}
</delete>

4.2 常用属性

属性名称 描述 备注
id 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常
parameterType 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 支持基本数据类型和 JavaBean、Map 等复杂数据类型
flushCache 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 -
timeout 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 -
statementType 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 -

4.3 多参数

和select标签规则一样

八、resultMap

​ resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。现有的 MyBatis 版本只支持 resultMap 查询,我们通过案例分析

1、构成

<resultMap id="" type="">
<constructor><!-- 类再实例化时用来注入结果到构造方法 -->
    <idArg/><!-- ID参数,结果为ID -->
    <arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
    <case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>

1.1 说明

  • <resultMap>元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识
  • 子元素 <constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。一般不用
  • 子元素 <id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
  • 子元素<result> 用于表示 POJO 和 SQL 列名的映射关系。
  • 子元素 <association><collection><discriminator> 用在级联的情况下。后续学习

id 和 result 元素都有以下属性

元素 说明
property 映射到列结果的字段或属性。如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上
column 对应 SQL 列
javaType 配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名
jdbcType 配置数据库类型。这是 JDBC 类型,MyBatis 已经为我们做了限定,基本支持所有常用数据库类型
typeHandler 类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器。需要指定 jdbcType 和 javaType 相互转化的规则

1.2 获取结果集的两种方式

1.2.1 使用Map存储结果集

不推荐使用该方式

  1. 接口
// 用户列表
List<Map<String,Object>> queryAllUser();
  1. Mapper
<select id="selectAllUser" resultType="map">
    select * from `user`
</select>

**注意事项:**Map 的 key 是 select 语句查询的字段名(必须完全一样),而 Map 的 value 是查询返回结果中字段对应的值,一条记录映射到一个 Map 对象中。

1.2.2 使用POJO存储结果集

推荐使用该方式

  1. POJO类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private int id;
    private String username;
    private String password;
}
  1. 接口
public interface UserMapper {
    
    
    List<User> getUserList();
}
  1. Mapper
<?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.hqyj.cl.dao.UserDao">
    <resultMap id="userMap" type="User">
        <id column="id" property="id"/>
        <result column="username" property="userName"/>
        <result column="password" property="password"/>
    </resultMap>
	<!--查询用户列表-->
    <select id="getUserList" resultMap="userMap">
        select * from `user`
    </select>
</mapper>
1.2.2.1 说明
  1. resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。
  2. 使用 property 元素指定 User的属性名称 username,column 表示数据库中 user表的 SQL 列名 username,将 POJO 和 SQL 的查询结果一 一对应。
  3. MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
  4. resultMap 和 resultType 不能同时使用。

九、注解

为了简化 XML 的配置,MyBatis 提供了注解,这些注解主要分为三大类,即 SQL 语句映射、结果集映射和关系映射。我们就逐一学习

1、SQL 语句映射

1.1 @Insert

实现新增功能

@Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age})")
int addUser(User user);

1.2 @SelectKey

插入后,获取id的值

@Insert("insert into user(id,name) values(#{id},#{name})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int.class,before = false)
int insert(User user);

@SelectKey 各个属性含义如下

  • statement:表示要运行的 SQL 语句;
  • keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
  • keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
  • resultType:指定 SQL 语句的返回值;
  • before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。

1.3 @Select

实现查询功能

增加templateMapper.xml文件在mapper包中 namespace属性空着

mybatis-config.xml中要扫描包

@Select("Select * from user")
@Results({
    
    
    @Result(id = true, column = "id", property = "id"),
    @Result(column = "name", property = "name"),
    @Result(column = "sex", property = "sex"),
    @Result(column = "age", property = "age")
})
List<User> queryAllUser();

@Results配置映射关系,如果mybatis-config.xml中关闭了驼峰命名,数据库字段与pojo类成员变量不能自动映射可以使用该注解

1.4 @Update

实现更新功能

@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);

1.5 @Delete

实现删除功能

@Delete("delete from  user  where id =#{id}")
void deleteById(Integer id);

1.6 @Param

用于在 Mapper 接口中映射多个参数,之前已经说明并演示

2、结果集映射

@Result、@Results、@ResultMap 是结果集映射的三大注解。

2.1 案例

@Select({
    
    "select id, name, class_id from student"})
@Results(id="studentMap", value={
    
    
    @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
    @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
    @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAllStudent();

@Results 各个属性的含义

  • id:表示当前结果集声明的唯一标识,可以通过id重复利用;
  • value:表示结果集映射关系;
  • @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。

使用 @ResultMap 来引用映射结果集,其中 value 可省略

@Select({
    
    "select id, name, class_id from student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(int id);

3、关系映射

基础学生类增加成员变量修改数据类型,记得要重构有参构造方法。

3.1 @one

用于一对一关系映射

@Select("select * from stu")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),
   @Result(property="addr",column="fk_addr_id",one=@One(select="com.hqyj.cl.mapper.StudentMapper.getAddr"))
})
List<Student> getAllStudents();

@Select("select * from addr where id = #{id}")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="addrName",column="addr_name")
})
Addr getAddr(int id);

3.2 @many

用于一对多关系映射

@Select("select * from stu")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),
  @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();

@Select("select * from teacher where id = #{id}")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="teacherName",column="teacher_name")
})
List<Teacher> getTeacher(int id);

3.3 多对多

在一对多的基础上,增加一个外表student_teacher,实现对student表和teacher表的关联

@Select("select * from stu")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="student_name",column="name"),    @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();

@Select("select * from teacher t,student_fk_teacher st where t.id = st.teacher_id and st.student_id = #{id}")
@Results({
    
    
    @Result(id=true,property="id",column="id"),
    @Result(property="teacherName",column="teacher_name"),
})
List<Teacher> getTeacher(int id);

十、MyBatis关联(级联)查询

级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。比如一个人对应一张身份证,一张身份证对应一个人、一个班级拥有多个学生,一个学生只能够属于某个班级、 一个学生可以选修多门课程,一个课程可以被多个学生选修。

1、一对一

在 MyBatis 中,通过 <resultMap> 元素的子元素 <association> 处理一对一级联关系。示例代码如下

POJO类和数据库设计,Student、Teacher、Addr三个类

  1. Student类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private int id;
    private String studentName;
    private Addr addr;
    private List<Teacher> teacher;
}
  1. Addr类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Addr {
    
    
    private int id;
    private String addr;
}
  1. 接口
List<Student> selectAllStudent();
  1. Mapper
<?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.hqyj.cl.mapper.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <id property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <association property="addr">
            <id property="id" column="id"/>
            <result property="addr" column="addr"/>
        </association>
    </resultMap>

    <select id="selectAllStudent" resultMap="studentMap">
        select * from `stu` s,`addr` a where s.fk_addr_id = a.id 
    </select>

</mapper>
  1. 测试
@Test
public void getAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.selectAllStudent();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(studentList);
}

<association> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

2、一对多

在 MyBatis 中,通过 <resultMap> 元素的子元素 <collection> 处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下

  1. Teacher类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    
    
    private int id;
    private String teacherName;
}
  1. 接口
List<Student> selectAllStudent();
  1. Mapper
<?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.hqyj.cl.mapper.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <id property="id" column="id"/>
        <result property="studentName" column="student_name"/>
        <collection property="teacher" ofType="teacher">
            <id property="id" column="id"/>
            <result property="teacherName" column="teacher_name"/>
        </collection>
    </resultMap>

    <select id="selectAllStudent" resultMap="studentMap">
        select * from `stu` s,`addr` a,`teacher` t where s.fk_teacher_id = t.id
    </select>

</mapper>
  1. 测试
@Test
public void getAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.selectAllStudent();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(studentList);
}

<collection> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

3、多对多

​ 实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

十一、MyBatis动态SQL

​ 动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。动态 SQL 大大减少了编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。MyBatis 也可以在注解中配置 SQL,但是由于注解功能受限,且对于复杂的 SQL 语句来说可读性差,所以使用较少。

1、动态SQL元素

MyBatis 的动态 SQL 包括以下几种元素

元素 作用 备注
if 判断语句 单条件分支判断
choose(when、otherwise) 相当于 Java 中的 switch case 语句 多条件分支判断
trim、where 辅助元素 用于处理一些SQL拼装问题
foreach 循环语句 在in语句等列举条件常用
bind 辅助元素 拼接参数

1.1 if标签

MyBatis if 类似于 Java 中的 if 语句,是 MyBatis 中最常用的判断语句。

1.1.1 格式

<if test="判断条件">
    <!--条件为true时执行SQL语句-->
    SQL语句
</if>

1.1.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user where 1=1
    <if test="name != null">
        and `name` like #{name}
    </if>
    <if test="state != 0">
        and state like #{state}
    </if>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("王五");
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.2 choose、when和otherwise标签

​ MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到<if><else></else> </if> 的效果,可以借助 <choose><when><otherwise> 来实现。

1.2.1 格式

​ choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,执行 otherwise 中的 SQL 语句。这类似于 Java 的 switch 语句,choose为switch,when 为 case,otherwise则为default。

<choose>
    <when test="判断条件1">
        SQL语句1
    </when >
    <when test="判断条件2">
        SQL语句2
    </when >
    <when test="判断条件3">
        SQL语句3
    </when >
    <otherwise>
        SQL语句4
    </otherwise>
</choose>

1.2.1 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user where 1=1
    <choose>
        <when test="name != null and name !=''">
            AND name LIKE CONCAT('%',#{name},'%')
        </when>
        <when test="state != 0 and state == 1">
            AND state = #{state}
        </when>
        <otherwise>
            AND password is not null
        </otherwise>
    </choose>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.3 where标签

​ 在上述标签SQL代码的编写中,我们不难发现,每一条SQL 语句中加入了一个条件“1=1”,如果没有加入这个条件,那么可能就会变成下面这样一条错误的语句。

SELECT * FROM user AND name LIKE CONCAT('%',#{name},'%')

显然以上语句会出现 SQL 语法异常,但加入“1=1”这样的条件又非常奇怪,所以 MyBatis 提供了 where 标签

1.3.1 格式

where 标签主要用来简化 SQL 语句中的条件判断,可以自动处理 AND/OR 条件,语法如下

<where>
    <if test="判断条件">
        AND/OR ...
    </if>
</where>

**注意事项:if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句**的 AND 或者 OR 关键词去掉。

1.3.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user
    <where>
        <if test="name != null">
            name like #{name}
        </if>
        <if test="state != 0">
            AND state like #{state}
        </if>
    </where>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
	user.setName("admin");
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

1.4 set标签

​ 在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号

1.4.1 案例

  1. 接口
int updateUser(User user);
  1. Mapper
<update id="updateUser">
    UPDATE user
    <set>
        <if test="password!=null">
            password=#{password}
        </if>
        <if test="state==0 || state==1">
            state=#{state}
        </if>
    </set>
    WHERE name=#{name}
</update>
  1. 测试
@Test
public void updateUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("admin");
    user.setState(1);
    int result = mapper.updateUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(result);
}

**注意事项:**if判断中必须成立一个,并且where后面的条件对应的值必须传递,否则报错

1.5 foreach标签

​ 对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。 Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下

<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
    参数值
</foreach>

foreach 标签主要有以下属性,说明如下。

  • item:表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选
  • index:在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。
  • open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
  • separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
  • close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。
  • collection表示迭代集合的名称,可以使用@Param注解指定,有以下四种情况
    • 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list;
    • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array;
    • 如果如果传入的是单参数且参数类型是一个Set集合的时候,可以通过注解@Param("set")指定key值;
    • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。必须加上注解@Param指定

1.5.1 案例

  1. 接口
List<User> selectUser(List<Integer> ageList);
  1. Mapper
<select id="selectUser" resultMap="userMap">
    SELECT * FROM user WHERE age in
    <foreach item="age" index="index" collection="list" open="("
             separator="," close=")">
        #{age}
    </foreach>
</select>
  1. 测试
@Test
public void selectUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<Integer> ageList = new ArrayList<>();
    ageList.add(1);
    ageList.add(12);
    ageList.add(13);
    List<User> result = mapper.selectUser(ageList);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(result);
}

1.6 bind标签

​ 每个数据库的拼接函数或连接符号都不同,例如 MySQL 的 concat 函数。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。

MyBatis bind 标签可以通过 OGNL(对象导航图语言) 表达式自定义一个上下文变量。

bind 元素属性如下

  • value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理。
  • name:给对应参数取的别名。

1.6.1 案例

  1. 接口
List<User> queryUser(String name);
  1. Mapper
<select id="queryUser" resultMap="userMap">
    <bind name="pattern" value="'%'+_parameter+'%'" />
    SELECT * FROM `user`
    WHERE name like #{pattern} 
</select>
  1. 测试
@Test
public void queryUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.queryUser("a");
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

**注意事项:**以上代码中的“_parameter”代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,提高了可移植性。

1.7 trim标签

​ 在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

1.7.1 格式

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    SQL语句
</trim>
1.7.1.1 属性说明
属性 描述
prefix 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
suffix 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
prefixOverrides 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
suffixOverrides 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定

1.7.2 案例

  1. 接口
List<User> selectAllUser(User user);
  1. Mapper
<select id="selectAllUser" resultMap="userMap">
    select * from user
    <trim prefix="where" prefixOverrides="and">
        <if test="name != null">
            and `name` like #{name}
        </if>
        <if test="state != 0">
            and state like #{state}
        </if>
    </trim>
</select>
  1. 测试
@Test
public void selectAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    List<User> userList = mapper.selectAllUser(user);
    // SqlSession执行文件中定义的SQL,并返回映射结果
    System.out.println(userList);
}

十二、MyBatis分页功能

MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。

1、pageHelper插件

在使用MyBatis分页功能时,通常和pageHelper插件配合使用

1.1 参数

PageInfo{
    
    
pageNum=1, 	//页数
pageSize=2, 	//页大小
size=2, 大小
startRow=1, 	//开始行
endRow=2, 	//结束行
total=6, 		//总条数
pages=3, 		//总页数
list=Page{
    
    count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=6, pages=3, reasonable=false, pageSizeZero=false}, 
prePage=0, 	//首页
nextPage=2, 	//下一页
isFirstPage=true, 	//是第一页
isLastPage=false,	// 是最后一页
hasPreviousPage=false, 	//有上一页
hasNextPage=true, 		//有下一页
navigatePages=2, 		//导航页数
navigateFirstPage=1, 	//导航到第一页
navigateLastPage=2,  	//导航到最后一页
navigatepageNums=[1, 2]}	//导航 页数 页码

1.2 使用步骤

  1. 导入依赖
<!--pageHelper-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.4</version>
</dependency>
  1. 配置mybatis-config.xml

在mybatis-config.xml中配置,注意顺序问题

<!-- 配置分页插件 -->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!-- 设置数据库类型-->
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>
  1. 接口
List<User> queryAllUser();
  1. Mapper

一定注意,不要在SQL语句后面加分号,否则MyBatis不能动态的正确拼接limt 分页语句

<select id="queryAllUser" resultMap="userMap">
    select * from user
</select>
  1. 测试
@Test
public void queryAllUser() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 开启分页,pageNum表示当前页码,pageSize表示每页数据条数
    PageHelper.startPage(2,2);
    // 获取数据
    List<User> userList = mapper.queryAllUser();
    // 把数据封装在pageInfo对象中,后期可以通过这种方式返回给前端,进行分页展示
    PageInfo<User> userPageInfo = new PageInfo<>(userList);
    System.out.println(userPageInfo);
}

十三、log4j

​ 在使用SSM进行开发时, 随着代码量的增大, 配置信息的繁杂, 排错也随之变得越来越难. 而日志就是我们debug的有力工具,常用的日志有很多, 我们选择使用log4j,下文介绍log4j在MyBatis下的简单应用

1、使用步骤

  1. 导入依赖
<!--log4j-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 配置mybatis-config.xml

在mybatis-config.xml中配置,注意顺序问题

<settings>
    <!-- 配置log4j日志 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. 创建 文件(直接在resource文件夹下创建)
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

十四、MyBatis缓存

​ 缓存可以将数据保存在内存中,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 开启二级缓存,但是并不是意味着我们之前写的所有sql都是二级缓存,要使用二级缓存需要在xml文件中使用<cache/>标签指出。

下面案例是演示配置了日志系统后,当你查询的条件一摸一样的时候只执行了一次sql。

1、一级缓存

  1. 一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
  2. 在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
  3. 由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

1.1 案例

  1. 保证日志开启,方便查看输出数据
  2. 接口
List<User> getUserList();
  1. Mapper
<select id="queryAllUser" resultMap="userMap">
    select * from user
</select>
  1. 测试
@Test
public void getUserList() throws IOException {
    
    
        // 读取配置文件mybatis-config.xml
        InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
        // 根据配置文件构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
        // 通过SqlSessionFactory创建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // SqlSession执行文件中定义的SQL,并返回映射结果
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        System.out.println(userList);
        List<User> userList1 = mapper.getUserList();
        System.out.println(userList1);
        System.out.println(userList1==userList);// true
}

日志打印信息

[com.hqyj.cl.mapper.UserMapper.getUserList]-==>  Preparing: select * from `user` 
[com.hqyj.cl.mapper.UserMapper.getUserList]-==> Parameters: 
[com.hqyj.cl.mapper.UserMapper.getUserList]-<==      Total: 3
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]
===========================================================================================================
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]

1.2 缓存失效

  • 查询不同的东西,比如同一个SQL语句,但是传递的参数不同。
  • 查询不同的Mapper.xml。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存,也就是说,在两条相同语句中间插入一条其他语句(比如inset),缓存会刷新。
  • 手动清理缓存。sqlSession.clearCache()
  • 把第一个创建的sqlSession通过sqlSession.close()方法关闭后,再创建一个sqlSession,也无法实现缓存。

1.3 总结

一级缓存基本上没啥用,因为用户执行的操作出现一模一样的情况很少;通过debug可以发现,其实就是把查询出来的值存入Map集合。

2、二级缓存

二级缓存就是与namespace级别的缓存,一个名称空间对应一个缓存。

2.1 工作机制

​ 一个会话查询一条数据,这个数据会存放在当前会话的一级缓存中,当会话结束时,一级缓存就会消失,但是我们可以把当前会话数据保存在二级缓存中,新的会话查询信息就可以从二级缓存中获取内容,当然,不同的mapper查询的数据都会放在自己对应的缓存中。

2.2 格式

要启用全局的二级缓存,只需要在你的 SQL 映射文件中(XXXMapper.xml)添加一行<cache/>,比如

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

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

配置详解

  • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
  • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
  • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

2.3 可用的清除策略

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

2.4 注意事项

  • 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新;
  • 不能使用封装获取sqlSession对象的类;

2.5 案例

  1. 开启全局缓存(mybatis-config.xml)配置二级缓存后可以尝试不同的sqlsession执行方法,控制台只打印一条sql语句,证明

    是从缓存中取出数据

<!--开启全局缓存,默认也是开启的,但是我们一般显示定义-->
<setting name="cacheEnabled" value="true"/>

**注意事项:**在CRUD标签中,可以使用useCache属性指定是否启用二级缓存,默认true。

  1. 接口
List<User> getUserList();
  1. mapper
<!--开启二级缓存-->
<cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
  1. 测试
@Test
public void getUserList() throws IOException {
    
    
    // 读取配置文件mybatis-config.xml
    InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
    // 根据配置文件构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
    // 通过SqlSessionFactory创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // SqlSession执行文件中定义的SQL,并返回映射结果
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserList();
    System.out.println(userList);
    // 关闭第一个sqlSession连接,它会把缓存放入二级缓存中
    sqlSession.close();
    System.out.println("=======================================================");
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    List<User> userList1 = mapper1.getUserList();
    System.out.println(userList1);
    System.out.println(userList1==userList);// true
    // 关闭第二个sqlSession连接
    sqlSession1.close();
}

2.6 错误及解决

  1. 错误

如果我们在mapper文件中,直接使用<cache/>方式开启二级缓存,会报错org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.hqyj.cl.pojo.User

  1. 解决

因为要使用二级缓存需要把对应的类序列化,所以解决办法如下

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    
    
    private int id;
    private String name;
    private String password;
    private int age;
    private int deptId;
    private int state;
}

十五、MyBatis逆向工程

Mybatis 提供了一个逆向工程工具,该工具可以根据数据表自动生成针对单表的 pojo 类、mapper 映射文件和 mapper 接口。

1、使用步骤

  1. 创建数据库
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `gender` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `role` varchar(255) NOT NULL DEFAULT '用户',
  `state` int(255) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
  1. 创建项目

新建项目,并且新建资源文件夹 config,在 config 文件夹下创建 generatorConfig.xml 文件,用于配置及指定数据库及表等。

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/servletdemo" userId="root"
                        password="112112" />

        <!-- 默认为false,把JDBC DECIMAL和NUMERIC类型解析为Integer;为true时 把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成POJO类的位置 -->
        <javaModelGenerator
                targetPackage="com.hqyj.cl.pojo" targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.hqyj.cl.mapper"
                         targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- targetProject:mapper接口生成的的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.hqyj.cl.mapper" targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <!-- 指定数据表 -->
        <table tableName="user"></table>
    </context>
</generatorConfiguration>

注意事项:targetProject写成绝对路径,否则可能出现路径不对,加载不出文件的情况

  1. 导入依赖
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.0</version>
</dependency>
  1. GeneratorSqlmap 类
package net.biancheng;

import java.io.File;
import java.util.*;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlMap {
    
    
    public void generator() throws Exception {
    
    
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;
        // 指定配置文件
        File configFile = new File("D:\\workspace\\mybatis\\mybatis_03\\src\\main\\resources\\config\\generator-config.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }

    // 执行main方法以生成代码
    public static void main(String[] args) {
    
    
        try {
    
    
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

注意事项:FILE文件路径写成绝对路径,相对路径可能存在路径不对的情况

2、说明

在 pojo 包中,有一部分是名字为 XxxExample 的类。类中包含以下 3 个成员变量

protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;;

以上变量说明如下:

  • distinct 字段用于指定 DISTINCT 查询。
  • orderByClause 字段用于指定 ORDER BY 条件,这个条件没有构造方法,直接通过传递字符串值指定。
  • oredCriteria 字段用于自定义查询条件。

Guess you like

Origin blog.csdn.net/ailaohuyou211/article/details/130394313