Mybatis is easy to get started, one article is enough

Table of contents

1. Introduction to MyBatis

1.0. What is MyBatis?

1.1 History of MyBatis

1.2, MyBatis features

1.3, MyBatis download

1.4. Comparison with other persistence layer technologies

2. Build MyBatis

2.1. Development environment

2.2. Create a maven project

2.3. Create the core configuration file of MyBatis

2.4. Create mapper interface

2.5. Create MyBatis mapping file

2.6, through the junit test function

2.7, add log4j log function

①Add dependency

②Add log4j configuration file

3. Detailed explanation of the core configuration file

4. Addition, deletion, modification and query of MyBatis

4.1. Added

4.2. Delete

4.3. Modification

4.4. Query an entity class object

4.5. Query list collection

5. Two ways for MyBatis to obtain parameter values

The difference between ${} and #{}

1. Symbol type

2. Anti-injection problem

3. SQL execution process

5.1. Parameters of a single literal type

5.2. Parameters of multiple literal types

5.3. Parameters of map collection type

5.4. Parameters of Entity Class Types

5.5. Use @Param to identify parameters

Summary of MyBatis getting parameter values

6. Various query functions of MyBatis

6.1. Query an entity class object

6.2. Query a list collection

6.3. Query a single data

6.4. Query a piece of data as a map collection

6.5. Query multiple pieces of data as a map collection

① Method 1

②Method 2

7. Execution of special SQL

7.1. Fuzzy query

7.2. Batch delete

7.3. Dynamically set the table name

7.4. Add function to get auto-incremented primary key

8. Custom mapping resultMap

8.1, resultMap handles the mapping relationship between fields and attributes

8.2. Many-to-one mapping processing

8.2.1, cascade processing mapping relationship

8.2.2. Use association to process mapping relationship

8.2.3. Step-by-step query

8.3, one-to-many mapping processing

8.3.1、collection

8.3.2. Step-by-step query

①Query department information

②Query all employees in the department according to the department id

9. Dynamic SQL

9.1、if

9.2、where

9.3、trim

9.4、choose、when、otherwise

9.5, foreach loop label

9.6, SQL fragments

10. MyBatis cache

10.1, MyBatis's first-level cache

10.2. Second level cache of MyBatis

10.3, related configuration of the second level cache

10.4. The order of MyBatis cache query

10.5. Integrate third-party cache EHCache

10.5.1. Add dependencies

10.5.2. Functions of each jar package

10.5.3. Create EHCache configuration file ehcache.xml

10.5.4. Set the type of L2 cache

10.5.5, add logback log

10.5.6, EHCache configuration file description

11. Reverse engineering of MyBatis

11.1. Steps to create reverse engineering

①Add dependencies and plug-ins

②Create the core configuration file of MyBatis

③Create a configuration file for reverse engineering

④ Execute the generate target of the MBG plug-in

⑤Effect

11.2. QBC query

12. Pagination plug-in

12.1. Steps to use the pagination plug-in

① Add dependencies

② Configure the pagination plug-in

12.2, the use of pagination plug-ins


1. Introduction to MyBatis

1.0. What is MyBatis?

MyBatis is a semi-automatic ORM (Object Relation Mapping) (object relational mapping) framework

Object: Java's entity class object

Relational: relational database

Mapping: the correspondence between the two

Java concepts database concept
kind surface
Attributes field name (column)
Object (an object corresponds to a row of data in a table in the database) record/line line

1.1 History of MyBatis

MyBatis was originally iBatis , an open source project of Apache . In June 2010, the project was migrated from Apache Software Foundation to Google Code. As the development team transferred to Google Code, iBatis3.x officially changed its name to MyBatis. The code was migrated to Github in November 2013.

The word iBatis comes from the combination of "internet" and "abatis", which is a Java-based persistence layer framework. The persistence layer framework provided by iBatis includes SQL Maps and Data Access Objects (DAO).

1.2, MyBatis features

1) MyBatis is an excellent persistence layer framework that supports customized SQL, stored procedures and advanced mapping

2) MyBatis avoids almost all JDBC code and manually setting parameters and getting result sets

3) MyBatis can use simple XML or annotations for configuration and original mapping, and interface and Java POJO (Plain Old Java

Objects, ordinary Java objects) are mapped to records in the database

4) MyBatis is a semi-automatic ORM (Object Relation Mapping) (object relational mapping) framework

Object: Java's entity class object

Relational: relational database

Mapping: the correspondence between the two

Java concepts database concept
kind surface
Attributes field name (column)
Object (an object corresponds to a row of data in a table in the database) record/line line

5) MyBatis is directly oriented to interface programming, without implementing implementation classes

Create the proxy implementation class object of the UserMapper interface through the proxy mode, mybatis is directly oriented to interface programming 
UserMapper mapper = sqlSession.getMapper(UserMapper.class);

1.3, MyBatis download

MyBatis download address: GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java

 

1.4. Comparison with other persistence layer technologies

  • JDBC

    • SQL is mixed in Java code with a high degree of coupling, resulting in hard-coded internal damage

    • It is not easy to maintain and SQL changes in actual development requirements, and frequent modifications are common

    • The code is lengthy and the development efficiency is low

  • Hibernate and JPA

    • Easy to operate and high development efficiency

    • Long, difficult and complex SQL in the program needs to bypass the framework

    • Internally automatically generated SQL, it is not easy to do special optimization

    • Based on the fully-automatic framework of full mapping, it is difficult to partially map POJOs with a large number of fields.

    • Too many reflection operations, resulting in a decline in database performance

  • MyBatis

    • Lightweight, great performance

    • SQL and Java coding are separated, and the functional boundaries are clear. Java codes focus on business, SQL statements focus on data

    • The development efficiency is slightly lower than that of HIbernate, but it is completely acceptable

2. Build MyBatis

2.1. Development environment

IDE: idea 2019.2

Build tool: maven 3.5.4

MySQL version: MySQL 8

MyBatis version: MyBatis 3.5.7

Notes on different versions of MySQL

1. Driver class driver-class-name

The MySQL 5 version uses the jdbc5 driver, and the driver class uses: com.mysql.jdbc.Driver

The MySQL 8 version uses the jdbc8 driver, and the driver class uses: com.mysql.cj.jdbc.Driver

2. Connection address url

URL for MySQL 5 version:

jdbc:mysql://localhost:3306/ssm

The url of MySQL 8 version:

jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC

Otherwise, running the test case reports the following error:

java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or

represents more

2.2. Create a maven project

① Packaging method: jar

②Introduction of dependencies

<dependencies>
   <!-- Mybatis核心 -->
   <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.5.7</version>
   </dependency>
    
<!-- junit测试 -->
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
       <scope>test</scope>
   </dependency>
    
<!-- MySQL驱动 -->
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>8.0.16</version>
   </dependency>
</dependencies>

2.3. Create the core configuration file of MyBatis

It is customary to name it mybatis-config.xml. This file name is just a suggestion, not a mandatory requirement. Integrate Spring in the future

Afterwards, this configuration file can be omitted, so you can copy and paste directly when you operate.

The core configuration file is mainly used to configure the environment for connecting to the database and the global configuration information of MyBatis

The location where the core configuration file is stored is in the src/main/resources directory

<?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"/>
            <!--    装配druid的数据源   数据源:管理数据库的链接的-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
​
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
​
</configuration>

2.4. Create mapper interface

The mapper interface in MyBatis is equivalent to the previous dao. But the difference is that mapper is just an interface, and we don't need to provide an implementation class.

public interface UserMapper {
​
    //添加用户
    int insertUser();
​
    //修改用户信息
    void updateUser();
​
    //删除用户信息
    void deleteUser();
​
    //根据id查询用户信息
    User getUserById();
​
    //查询所有的用户信息
    List<User> getAllUser();
}

2.5. Create MyBatis mapping file

Related concepts: ORM ( Object Relationship M apping) object-relational mapping .

  • Object: Java's entity class object

  • Relational: relational database

  • Mapping: the correspondence between the two

Java concepts database concept
kind surface
Attributes field/column
Object (an object corresponds to a row of data in the database) record/line

1. Naming rules for mapping files:

The class name of the entity class corresponding to the table + Mapper.xml

For example: table t_user, the mapped entity class is User, and the corresponding mapping file is UserMapper.xml

Therefore, a mapping file corresponds to an entity class, corresponding to the operation of a table

MyBatis mapping files are used to write SQL, access and manipulate data in tables

The location where the MyBatis mapping file is stored is in the src/main/resources/mappers directory

2. In MyBatis, data can be operated on the interface , and the two must be consistent:

a> The full class name of the mapper interface is consistent with the namespace of the mapping file

b> The method name of the method in the mapper interface is consistent with the id attribute of the label that writes SQL in the mapping file

<?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">
​
    <!--
    MyBatis中可以面向接口操作数据,要保证两个一致:
    a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
    b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
    -->
<mapper namespace="com.yka.mybatis.mapper.UserMapper">
    <!--int insertUser();-->
    <insert id="insertUser">
        insert into t_user values(null,'admin','123456',23,'男','[email protected]')
    </insert>
​
    <!--void updateUser();-->
    <update id="updateUser">
        update t_user set username = 'root',password = '123' where id = 3;
    </update>
​
    <!--void deleteUser();-->
    <delete id="deleteUser">
        delete from ssm.t_user where id = 4
    </delete>
​
    <!--
        查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系
        resultType:自动映射,用于属性名和表中字段名一致的情况,查询的数据结果要转化为Java类型
        resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
    -->
​
    <!--User getUserById();-->
    <select id="getUserById" resultType="com.yka.mybatis.pojo.User">
        select * from ssm.t_user where id = 1;
    </select>
​
    <!--List<User> getAllUser();-->
    <select id="getAllUser" resultType="com.yka.mybatis.pojo.User">
        select * from ssm.t_user;
    </select>
</mapper>

2.6, through the junit test function

First create the tool class SqlSessionUtil to simplify the code of the following test classes

public class SqlSessionUtil {
    public static SqlSession getSqlSession(){
            SqlSession sqlSession = null;
        try {
            //获取核心配置文件的输入流
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //获取SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            //获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sqlSession;
    }
}
public class MybatisTest {
​
    //新增
    @Test
    public void testInsert() throws IOException {
        //获取核心配置文件的输入流
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        System.out.println(is);
​
        //获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        System.out.println(sqlSessionFactoryBuilder);
​
        //通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        System.out.println(sqlSessionFactory);
​
        //获取sql的会话对象SqlSession(不会自动提交事物),是MyBatis提供的操作数据库的对象
        //SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取sql的会话对象SqlSession(true会自动提交事物),是MyBatis提供的操作数据库的对象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
​
        //两种写法执行sql语句的,第二种不常用了解就行
        //第一种:通过代理模式 创建UserMapper接口的代理实现类对象 ,mybatis直接面向接口编程
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配映射文件,通过调用的方法名匹配映射文件中的SQL标签的       id,并执行标签中的SQL语句
        int result = mapper.insertUser();
​
        //第二种:提交sql以及的唯一标识找到sql并执行,唯一标识是映射文件中 两个标签属性:namespace.splId
        //int result = sqlSession.insert("com.yka.mybatis.mapper.UserMapper.insertUser");
        System.out.println("结果:"+result);
        //提交事物
//        sqlSession.commit();
        //关闭sqlSession
        sqlSession.close();
    }
​
    //修改
    @Test
    public void testUpdate(){
        //调用SqlSessionUtil工具类的方法
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        //通过代理模式创建UserMapper接口的代理实现类对象,mybatis直接面向接口编程
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配映射文件中的namespace属性,通过调用的方法名与映射文件       中的SQL标签的id匹配,并执行标签中的SQL语句
        mapper.updateUser();
        sqlSession.close();
    }
​
    //删除
    @Test
    public void testDelete(){
        //调用SqlSessionUtil工具类的方法
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        //通过代理模式创建UserMapper接口的代理实现类对象,mybatis直接面向接口编程
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配映射文件中的namespace属性,通过调用的方法名与映射文件       中的SQL标签的id匹配,并执行标签中的SQL语句
        mapper.deleteUser();
        sqlSession.close();
    }
​
    //根据id查询
    @Test
    public void testGetUserById(){
        //调用SqlSessionUtil工具类的方法
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        //通过代理模式创建UserMapper接口的代理实现类对象,mybatis直接面向接口编程
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配映射文件中的namespace属性,通过调用的方法名与映射文件       中的SQL标签的id匹配,并执行标签中的SQL语句
        User user = mapper.getUserById();
        System.out.println(user);
        sqlSession.close();
    }
​
    //查询所有用户信息
    @Test
    public void testGetAllUser(){
        //调用SqlSessionUtil工具类的方法
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        //通过代理模式创建UserMapper接口的代理实现类对象,mybatis直接面向接口编程
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配映射文件中的namespace属性,通过调用的方法名与映射文件       中的SQL标签的id匹配,并执行标签中的SQL语句
        List<User> list = mapper.getAllUser();
        list.forEach(v->{
            System.out.println(v);
        });
        System.out.println(list);
        sqlSession.close();
    }
​
}
  • SqlSession: represents the session between the Java program and the database . (HttpSession is a session between a Java program and a browser)

  • SqlSessionFactory: It is a "factory" that "produces" SqlSession.

  • Factory mode: If the process of creating an object is basically fixed, then we can create the object

The relevant code is encapsulated into a "factory class", and this factory class will be used to "produce" the objects we need in the future.

2.7, add log4j log function

①Add dependency

<!-- log4j日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

②Add log4j configuration file

The log4j configuration file is named log4j.xml, and is stored in the src/main/resources directory

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}%m (%F:%L) \n" />
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug" />
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

log level

FATAL (fatal)>ERROR (error)>WARN (warning)>INFO (information)>DEBUG (debugging)

The content printed from left to right becomes more and more detailed

3. Detailed explanation of the core configuration file

Configure the jdbc.properties file

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

The tags in the core configuration file must be in a fixed order:

properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,refl

ectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

<?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>
    <!--
      MyBatis核心配置文件中,标签的顺序:
      properties?,settings?,typeAliases?,typeHandlers?,
      objectFactory?,objectWrapperFactory?,reflectorFactory?,
      plugins?,environments?,databaseIdProvider?,mappers?
    -->
    <!--引入properties文件,此后就可以在当前文件中使用${key}的方式访问value-->
    <properties resource="jdbc.properties" />
 
    <!--
     typeAliases:设置类型别名,即为某个具体的类型设置一个别名
     在MyBatis的范围中,就可以使用别名表示一个具体的类型,
     在映射文件中select标签中resultType属性中可以用别名来表示
 -->
    <typeAliases>
    <!--
        typeAlias:设置某个类型的别名
        属性:
            type:设置需要设置别名的类型
            alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,就是类名且不区分大小写
    -->
        <!--<typeAlias type="com.atguigu.mybatis.pojo.User"></typeAlias>-->
        <!--以包为单位,将包下所有的类型设置默认的类型别名,就是类名且不区分大小写-->
        <package name="com.yka.mybatis.pojo"/>
    </typeAliases>
    <!--
        environments:配置多个连接数据库的环境
        属性:
            default:设置默认使用的环境的id
    -->
    <environments default="development">
<!--
        environment:设置一个具体的连接数据库的环境
        属性:
            id:表示连接数据库的环境的唯一标识,不能重复
    -->
    <environment id="development">
        <!--
            transactionManager:设置事务管理方式
            属性:
                type="JDBC|MANAGED"
                JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理
                MANAGED:被管理,例如Spring
        -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource:配置数据源
                属性:
           type:设置数据源的类型
           type="POOLED|UNPOOLED|JNDI"
              POOLED:表示使用数据库连接池缓存数据库连接
              UNPOOLED:表示不使用数据库连接池
              JNDI:表示使用上下文中的数据源
            -->
                <dataSource type="POOLED">
           <!--设置连接数据库的驱动-->
           <property name="driver" value="${jdbc.driver}"/>
              <!--设置连接数据库的连接地址-->
              <property name="url" value="${jdbc.url}"/>
              <!--设置连接数据库的用户名-->
              <property name="username" value="${jdbc.username}"/>
              <!--设置连接数据库的密码-->
              <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
     
     <!--已经默认development,所以这里没用了-->
        <environment id="test">
       <transactionManager type="JDBC"/>
       <dataSource type="POOLED">
          <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url"value="jdbc:mysql://localhost:3306/ssmserverTimezone=UTC"/>
          <property name="username" value="root"/>
          <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
    <!--<mapper resource="mappers/UserMapper.xml"/>-->
    <!--
   以包为单位引入映射文件
      要求:
      1、mapper接口所在的包要和映射文件所在的包一致
         注:映射文件包这样创建:在resources下创建Directory:com/yka/mybatis/mapper
      2、mapper接口要和映射文件的名字一致
    -->
        <package name="com.yka.mybatis.mapper"/>
    </mappers>
</configuration>

4. Addition, deletion, modification and query of MyBatis

4.1. Added

<!--int insertUser();-->
<insert id="insertUser">
    insert into t_user values(null,'admin','123456',23,'男')
</insert>

4.2. Delete

<!--int deleteUser();-->
<delete id="deleteUser">
    delete from t_user where id = 7
</delete>

4.3. Modification

<!--int updateUser();-->
<update id="updateUser">
    update t_user set username='ybc',password='123' where id = 6
</update>

4.4. Query an entity class object

<!--User getUserById();-->
<select id="getUserById" resultType="com.atguigu.mybatis.bean.User">
    select * from t_user where id = 2
</select>

4.5. Query list collection

<!--List<User> getUserList();-->
<select id="getUserList" resultType="com.atguigu.mybatis.bean.User">
    select * from t_user
</select>

Notice:

1. The label select of the query must set the attribute resultType or resultMap, which is used to set the mapping relationship between the entity class and the database table

resultType: automatic mapping, used when the attribute name is consistent with the field name in the table, and the query data result should be converted to Java type (entity class or literal type)

resultMap: custom mapping, used for one-to-many or many-to-one or inconsistent field names and attribute names

5. Two ways for MyBatis to obtain parameter values

Two ways for MyBatis to get parameter values: ${} and #{}

The essence of ${} is string concatenation (passed over is real data), the essence of #{} is placeholder assignment precompilation (passed over is a placeholder?)

${} uses string splicing to splice sql. If it is a field of string type or date type, you need to add single quotes manually; ${}: without quotes means the passed data, without any quotes

However, #{} uses the way of placeholder assignment to splice sql. At this time, when assigning a value to a field of string type or date type,

Single quotes can be added automatically

The difference between ${} and #{}

1. Symbol type

(1) #{}: parameter placeholder, that is, precompiled (2) ${}: string splicing, that is, SQL splicing

2. Anti-injection problem

(1) #{}: Can prevent SQL injection to a large extent (2) ${}: Cannot prevent SQL injection

3. SQL execution process

(1) #{}: Compile the SQL statement before fetching the value (2) ${}: Compile the SQL statement after fetching the value

5.1. Parameters of a single literal type

If the method parameter in the mapper interface is a single literal type (string, int...: is a literal, except for the class type (entity class object), the others are literal)

At this point, you can use ${} and #{} to get the value of the parameter with any name. Note that ${} needs to be manually added with single quotes

select * from ssm.t_user where username = #{username}
select * from ssm.t_user where username = '${username}'

5.2. Parameters of multiple literal types

If there are multiple method parameters in the mapper interface

At this time, MyBatis will automatically put these parameters in a map collection, with arg0, arg1... as the key, and the parameter as the value;

param1, param2... are the keys, and the parameters are the values; therefore, you only need to access the keys of the map collection through ${} and #{} to get the relevant

Corresponding value, note that ${} needs to be manually added with single quotes

select * from ssm.t_user where username = '${arg0}' and password = '${arg1}'
select * from ssm.t_user where username = #{arg0} and password = #{arg1}
select * from ssm.t_user where username = '${param1}' and password = '${param2}'
select * from ssm.t_user where username = #{param1} and password = #{param2}

5.3. Parameters of map collection type

If the method in the mapper interface requires multiple parameters, you can manually create a map collection and put the data in the

in map

You only need to access the key of the map collection through ${} and #{} to get the corresponding value. Note that ${} needs to be manually added with single quotes

5.4. Parameters of Entity Class Types

If the method parameter in the mapper interface is an entity class object

At this time, you can use ${} and #{} to get the attribute value by accessing the attribute name in the entity class object. Note that ${} needs to be manually added with single quotes

Attribute name: It has nothing to do with member variables. The attribute name is the get and set method name. Remove the get and set and the remaining lowercase letters are the attribute name

insert into ssm.t_user values (null,#{username},#{password},#{age},#{gender},#{email})

5.5. Use @Param to identify parameters

The @Param annotation can be set on the parameters of the mapper interface method

At this point, these parameters will be placed in the map collection and stored in two ways:

a> Use the value attribute of the @Param annotation as the key and the parameter as the value

b> Use param1, param2... as the key, and take the parameter as the value

You only need to access the key of the map collection through ${} and #{} to get the corresponding value. Note that ${} needs to be manually added with single quotes

<!--select * from ssm.t_user where username = #{param1} and password = #{param2}-->
select * from ssm.t_user where username = #{username} and password = #{password}

Summary of MyBatis getting parameter values

To sum up, the above five methods of obtaining parameters only need to remember two:

1. If the method parameters in the mapper interface are one or more parameters, use the @Param annotation to identify the parameters uniformly.

The value attribute value of the @Param annotation is the key, and the parameter is the value

2. If the method parameter in the mapper interface is an entity class object, obtain the attribute value by accessing the attribute name in the entity class object

6. Various query functions of MyBatis

6.1. Query an entity class object

/**
* 根据用户id查询用户信息
* @param id
* @return
*/
User getUserById(@Param("id") int id);
<!--User getUserById(@Param("id") int id);-->
<select id="getUserById" resultType="User">
    select * from t_user where id = #{id}
</select>

6.2. Query a list collection

/**
* 查询所有用户信息
* @return
*/
List<User> getUserList();
<!--List<User> getUserList();-->
<select id="getUserList" resultType="User">
    select * from t_user
</select>

When the queried data is multiple, the entity class cannot be used as the return value, otherwise an exception will be thrown

TooManyResultsException; but if there is only one piece of query data, you can use entity class or collection as the return value

6.3. Query a single data

/**
* 查询用户的总记录数
* @return
* 在MyBatis中,对于Java中常用的类型都设置了类型别名
* 例如: java.lang.Integer-->int|integer
* 例如: int-->_int|_integer
* 例如: Map-->map,
  例如: List-->list
  例如: String-->string
*/
int getCount();
<!--int getCount();-->
<select id="getCount" resultType="_integer">
    select count(id) from t_user
</select>

6.4. Query a piece of data as a map collection

/**
* 根据用户id查询用户信息为map集合
* @param id
* @return
*/
Map<String, Object> getUserToMap(@Param("id") int id);
<!--Map<String, Object> getUserToMap(@Param("id") int id);-->
<!--
    结果: {password=123456, sex=男 , id=1, age=23, username=admin}
    如果结果集类型设置为map的类型,与实体类无关,以表的字段名为键,字段值为值
-->
<select id="getUserToMap" resultType="map">
    select * from t_user where id = #{id}
</select>

6.5. Query multiple pieces of data as a map collection

① Method 1

/**
* 查询所有用户信息为map集合
* @return
* 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此
时可以将这些map放在一个list集合中获取
*/
List<Map<String, Object>> getAllUserToMap();
<!--Map<String, Object> getAllUserToMap();-->
<select id="getAllUserToMap" resultType="map">
    select * from t_user
</select>

②Method 2

/**
* 查询所有用户信息为map集合
* @return
* 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并
且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的
map集合
*/
@MapKey("id")
Map<String, Object> getAllUserToMap();
<!--Map<String, Object> getAllUserToMap();-->
<!--
{
    1={password=123456, sex=男, id=1, age=23, username=admin},
    2={password=123456, sex=男, id=2, age=23, username=张三},
    3={password=123456, sex=男, id=3, age=23, username=张三}
}
-->
<select id="getAllUserToMap" resultType="map">
    select * from t_user
</select>

7. Execution of special SQL

7.1. Fuzzy query

/**
* 测试模糊查询
* @param mohu
* @return
*/
List<User> testMohu(@Param("mohu") String mohu);
<!--List<User> testMohu(@Param("mohu") String mohu);-->
<select id="testMohu" resultType="User">
    <!--select * from t_user where username like '%${mohu}%'-->
    <!--select * from t_user where username like concat('%',#{mohu},'%')-->
    select * from t_user where username like "%"#{mohu}"%"
</select>

7.2. Batch delete

/**
* 批量删除
* @param ids
* @return
*/
int deleteMore(@Param("ids") String ids);
<!--int deleteMore(@Param("ids") String ids);-->
<!--${}:不带引号就是传过来的数据,不带任何引号,所有传过来的就是7,8-->
<delete id="deleteMore">
    delete from t_user where id in (${ids})
</delete>
 
 
//测试类
int i = mapper.deleteMoreUser("7,8");

7.3. Dynamically set the table name

/**
* 动态设置表名,查询所有的用户信息
* @param tableName
* @return
*/
List<User> getAllUser(@Param("tableName") String tableName);
<!--List<User> getAllUser(@Param("tableName") String tableName);-->
<select id="getAllUser" resultType="User">
    select * from ${tableName}
</select>

7.4. Add function to get auto-incremented primary key

Scenario simulation:

t_clazz(clazz_id,clazz_name)

t_student(student_id,student_name,clazz_id)

1. Add class information

2. Get the id of the newly added class

3. Assign students to the class, that is, change the class id of a certain school to the id of the newly added class

/**
* 添加用户信息
* @param user
* @return
* useGeneratedKeys:设置使用自增的主键
* keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
*/
int insertUser(User user);
<!--int insertUser(User user);-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    insert into t_user values(null,#{username},#{password},#{age},#{sex})
</insert>
@Test
public void testInsertUser(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
    User user = new User(null,"xiaoming","123456",15,"男","123@qq");
    //执行方法之前没有获取到主键,id为null
    //结果:执行方法之前:User{id=null, username='xiaoming', password='123456', age=15, gender='男', email='123@qq'}
    System.out.println("执行方法之前:"+user);
    mapper.insertUser(user);
    //执行方法之后获取到了自增主键,并赋值到id上
    //结果:执行方法之后:User{id=11, username='xiaoming', password='123456', age=15, gender='男', email='123@qq'}
    System.out.println("执行方法之后:"+user);
}

8. Custom mapping resultMap

Create entity class

public class Emp {
    private Integer empId;
    private String empName;
    private Integer age;
    private String gender;
​
    //员工对部门是多对一,对一对应的是对象,部门对员工是一对多,对多对应的是集合
    private Dept dept;
}
public class Dept {
    private Integer deptId;
    private String deptName;
    
    //员工对部门是多对一,对一对应的是对象,部门对员工是一对多,对多对应的是集合
    private List<Emp> emps;
}

8.1, resultMap handles the mapping relationship between fields and attributes

If the field name is inconsistent with the attribute name in the entity class, you can set a custom map through resultMap

<!--
    resultMap:设置自定义映射关系
    属性:
    id:表示自定义映射的唯一标识
    type:处理映射关系的实体类的类型
​
    子标签:
    id:处理主键和实体类中属性名的映射关系
    result:处理普通字段和实体类中属性名的映射关系
    association:处理多对一的映射关系(处理实体类类型的属性)
    collection:处理一对多的映射关系(处理集合类型的属性)
​
    属性:
    column:设置映射关系中表中的字段名,必须是sql查询出的某个字段
    property:设置映射关系中实体类中的属性名,必须是处理映射关系的实体类类型(type属性)中的属性名
    JavaType:处理当前属性的类型
    select:设置分步查询,执行下一步的sql语句,返回的结果集赋值到property属性中,查询某个属性的值的sql的标识                      (namespace.sqlId)
    column:在association标签中使用时:将查询出结果(当前表)的某个字段设置为下一步分步查询的条件
-->
    <resultMap id="empResultMap" type="emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
    </resultMap>
​
    <!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
    <select id="getEmpByEmpId" resultMap="empResultMap">
        <!--select emp_id empId,emp_name empName,age,gender from t_emp where emp_id = #{empId}-->
        select * from t_emp where emp_id = #{empId}
    </select>

Test class:

//使用resultMap自定义映射处理字段名和属性名不一致
@Test
public void testGetEmpByEmpId(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = mapper.getEmpByEmpId(1);
    System.out.println(emp);
}

If the field name is inconsistent with the attribute name in the entity class, but the field name conforms to the rules of the database (use _), the attribute name in the entity class conforms to the rules of Java (use camel case)

At this time, the mapping relationship between the field name and the attribute in the entity class can also be processed in the following three ways

a> You can alias the field to ensure that it is consistent with the attribute name in the entity class

b> Customize the mapping through resultMap

c> You can set a global configuration information mapUnderscoreToCamelCase in the core configuration file of MyBatis, which can automatically convert the field name of type _ to camel case when querying data in the table

For example: the field name user_name, set mapUnderscoreToCamelCase, then the field name will be converted to

userName

<settings> 
<!--Map underscore to camelCase--> 
<setting name="mapUnderscoreToCamelCase" value="true"/> 
</settings>

8.2. Many-to-one mapping processing

Scenario simulation:

Query employee information and the department information corresponding to the employee

Employees are many-to-one to departments, one-to-one corresponds to objects, departments are one-to-many to employees, and one-to-many corresponds to collections

public class Emp { 
 private Integer empId; 
 private String empName; 
 private Integer age; 
 private String gender; 
​//
 Employees are many-to-one to departments, one-to-one corresponds to objects, departments are one-to-many to employees, and one-to-many corresponds is the collection 
 private Dept dept; 
}
public class Dept { 
 private Integer deptId; 
 private String deptName; 
 
 //Employees are many-to-one to departments, one-to-one corresponds to objects, departments are one-to-many to employees, and one-to-many corresponds to collections 
 private List<Emp> emps; 
}

8.2.1, cascade processing mapping relationship

<resultMap id="empAndDeptResultMap" type="emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <result column="dept_id" property="dept.deptId"></result>
        <result column="dept_name" property="dept.deptName"></result>
    </resultMap>
    <!--Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);-->
    <select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
        select emp.*,dept.* from t_emp emp left join t_dept dept on emp.emp_id =dept.dept_id where emp.emp_id = #{empId}
    </select>

8.2.2. Use association to process mapping relationship

<resultMap id="empAndDeptResultMap" type="emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
     <!--
            association:设置多对一的映射关系(处理实体类类型的属性)
            property:设置映射关系中实体类中的属性的属性名
            javaType:设置当前属性的类型
        -->
        <association property="dept" javaType="Dept">
            <id column="dept_id" property="deptId"></id>
            <result column="dept_name" property="deptName"></result>
        </association>
    </resultMap>
    <!--Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);-->
    <select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
        select emp.*,dept.* from t_emp emp left join t_dept dept on emp.emp_id =dept.dept_id where emp.emp_id = #{empId}
    </select>

test class

//多对一使用association和级联处理映射关系查询测试
@Test
public void testGetEmpAndDeptByEmpId(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp empAndDeptByEmpId = mapper.getEmpAndDeptByEmpId(1);
    System.out.println(empAndDeptByEmpId);
}

8.2.3. Step-by-step query

①Query employee information

/**
* 通过分步查询查询员工以及所对应的部门信息的第一步
* @param eid
* @return
*/
 Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);
<resultMap id="empAndDeptByStepResultMap" type="emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <!--
            association:设置多对一的映射关系(处理实体类类型的属性)
            property:设置映射关系中实体类中的属性的属性名
            select:设置分步查询,执行下一步的sql语句,返回的结果集赋值到dept属性中,查询某个属性的值的sql的标识(全类名加方法)(namespace.sqlId)
            column:将查询出结果(当前员工表)的某个字段设置为下一步分步查询的条件
            fetchType:在开启了延迟加载的环境中,此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,
            fetchType="lazy(延迟加载)|eager(立即加载)"
        -->
        <association fetchType="eager" property="dept" select="com.yka.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTow" column="dept_id"></association>
    </resultMap>
    <!--Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
    <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
        select * from t_emp where emp_id = #{empId}
    </select>

②Query department information according to the department id corresponding to the employee

/**
* 通过分步查询查询员工以及对应的部门信息的第二步
* @param did
* @return
*/
 Dept getEmpAndDeptByStepTow(@Param("deptId") String deptId);
<!--    Dept getEmpAndDeptByStepTow(@Param("deptId") String deptId);-->
    <select id="getEmpAndDeptByStepTow" resultType="dept">
        select * from t_dept where dept_id = #{deptId}
    </select>

Test class:

//多对一分步查询
@Test
public void testGetEmpAndDeptByStep(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    Emp empAndDeptByStepOne = mapper.getEmpAndDeptByStepOne(1);
    System.out.println(empAndDeptByStepOne);
}

8.3, one-to-many mapping processing

8.3.1、collection

/**
* 查询部门以及部门中的员工信息
* @param did
* @return
*/
 Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);
<resultMap id="deptAndEmpResultMap" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <!--
           collection:处理一对多的映射关系(处理集合类型的属性)
            ofType:设置collection标签所处理的集合属性中存储数据的类型(泛型类型)
        -->
        <collection property="emps" ofType="Emp">
            <id column="emp_id" property="empId"></id>
            <result column="emp_name" property="empName"></result>
            <result column="age" property="age"></result>
            <result column="gender" property="gender"></result>
        </collection>
    </resultMap>
    <!--Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);-->
    <select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
        select * from t_dept dept LEFT JOIN t_emp emp on dept.dept_id = emp.dept_id where dept.dept_id = #{deptId};
    </select>

Test class:

//一对多使用association处理映射关系查询
@Test
public void testGetDeptAndEmpByDeptId(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    Dept deptAndEmpByDeptId = mapper.getDeptAndEmpByDeptId(1);
    System.out.println(deptAndEmpByDeptId);
}

8.3.2. Step-by-step query

①Query department information

/**
* 通过分步查询查询部门信息以及部门中的员工信息的第一步
* @param did
* @return
*/
Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);
<resultMap id="deptAndEmpResultMapByStep"  type="dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <!--
            collection:处理一对多的映射关系(处理集合类型的属性)
            property:设置映射关系中实体类中的属性的属性名
            select:设置分步查询,执行下一步的sql语句,返回的结果集赋值到dept属性中,查询某个属性的值的sql的标识(全类名加方法)(namespace.sqlId)
            column:将查询出结果(当前部门表)的某个字段设置为下一步分步查询的条件
        -->
        <collection property="emps"
                    select="com.yka.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                    column="dept_id"
                    ></collection>
    </resultMap>
​
    <!--Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);-->
    <select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpResultMapByStep">
        select * from t_dept where dept_id = #{deptId}
    </select>

②Query all employees in the department according to the department id

/**
* 通过分步查询查询部门信息以及部门中的员工信息的第二步
* @param did
* @return
*/
List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);
 <!--List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);-->
    <select id="getDeptAndEmpByStepTwo" resultType="emp">
        select * from t_emp where dept_id = #{deptId}
    </select>

Test class:

//一对多分步查询
@Test
public void testGetDeptAndEmpByStep(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
    Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1);
    System.out.println(deptAndEmpByStepOne);
}

Advantages of step-by-step queries: lazy loading can be achieved, and memory usage can be reduced

But global configuration information must be set in the core configuration file:

<settings> 
 <!--Map underscore to camelCase--> 
 <setting name="mapUnderscoreToCamelCase" value="true"/> 
 <!--Enable lazy loading--> 
 <setting name="lazyLoadingEnabled" value="true "/> 
 <!--On-demand loading--> 
 <setting name="aggressiveLazyLoading" value="false"/> 
</settings>

lazyLoadingEnabled: Global switch for lazy loading. When enabled, all associated objects will be lazy loaded,

Which sql is used will execute the corresponding sql, and if it is not used, it will not execute, which can reduce the use of memory

aggressiveLazyLoading: When enabled, any method call will load all properties of the object, and all associated objects will be executed.

After closing, each attribute will be loaded on demand. At this time, on-demand loading can be realized. What is the obtained data, only the corresponding sql will be executed.

At this time, you can set whether the current step-by-step query uses lazy loading through the fetchType attribute in association and collection.

fetchType="lazy (lazy loading)|eager (immediate loading)"

9. Dynamic SQL

The dynamic SQL technology of the Mybatis framework is a function of dynamically assembling SQL statements according to specific conditions. The meaning of its existence is to

Solve the pain point problem when splicing SQL statement strings.

9.1、if

The if tag can be judged by the expression of the test attribute. If the result of the expression is true, the content in the tag will be executed; otherwise

The content in the tag will not execute <if test="entity class attribute">

<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="emp">
        select <include refid="empColumns"></include> from t_emp where 1=1
        <if test="empName != null and empName != ''">
            emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="gender != null and gender != ''">
            and gender = #{gender}
        </if>
  </select>

9.2、where

Where and if are generally used in combination:

a> If none of the if conditions in the where tag are met, the where tag has no function, that is, the where keyword will not be added

b> If the if condition in the where tag is met, the where tag will automatically add the where keyword, and put the excess at the front of the condition

and remove

Note: the where tag cannot remove the redundant and at the end of the condition

  <!--List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="emp">
        select <include refid="empColumns"></include> from t_emp
        <where>
            <if test="empName != null and empName != ''">
                and emp_name = #{empName}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="gender != null and gender != ''">
                and gender = #{gender}
            </if>
        </where>
    </select>

9.3、trim

trim is used to remove or add content in the label

Common attributes:

prefix: Add something in front of the content in the trim tag

prefixOverrides: Remove some content in front of the content in the trim tag

suffix: Add something after the content in the trim tag

suffixOverrides: Remove some content after the content in the trim tag

 <!--List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="emp">
        select <include refid="empColumns"></include> from t_emp
        <trim prefix="where" suffixOverrides="and">
            <if test="empName != null and empName != ''">
                emp_name = #{empName} and
            </if>
            <if test="age != null and age != ''">
                age = #{age} and
            </if>
            <if test="gender != null and gender != ''">
                gender = #{gender} and
            </if>
        </trim>
    </select>

The test class for the above three tags

//if where trim 三个标签测试
@Test
public void testGetEmpByCondition(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
    Emp emp = new Emp(null, "", null, "男");
    List<Emp> empByCondition = mapper.getEmpByCondition(emp);
    System.out.println(empByCondition);
}

9.4、choose、when、otherwise

choose、when、 otherwise

Equivalent to if...else if..else as long as if is true, else if and else will not be executed

If the first when tag is true, the following tags will not be executed, and if the first when tag is not true, the next tag will be judged

when set at least one, otherwise set at most one

choose, when, otherwise: at most one condition is executed, when is followed by one condition

 <!--List<Emp> getEmpByChoose(Emp emp);-->
    <select id="getEmpByChoose" resultType="emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name = #{empName}
                </when>
                <when test="age != null and age != ''">
                     age = #{age}
                </when>
                <when test="gender != null and gender != ''">
                     gender = #{gender}
                </when>
                <otherwise>
                    <!--以上都不成立执行这里-->
                </otherwise>
            </choose>
        </where>
    </select>

Test class:

//choose、when、otherwise测试
@Test
public void testGetEmpByChoose(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
    Emp emp = new Emp(null, "", null, "男");
    List<Emp> empByCondition = mapper.getEmpByChoose(emp);
    System.out.println(empByCondition);
}

9.5, foreach loop label

collection: Set the current loop to be a collection or array, mybatis will put the collection or array into the map collection, and access the collection or array through the value of @Param("") as the key.

item: Use a string to represent each piece of data in the collection or array, each item

separator: separator, the separator between the data of each cycle

open: What to start all content before the current loop

close: what ends the current loop ends everything

 <!--批量添加:void insertMoreEmp(@Param("emps") List<Emp> emps);-->
    <insert id="insertMoreEmp">
        insert into t_emp values
        <foreach collection="emps" item="emp" separator=",">
            (null,#{emp.empName},#{emp.age},#{emp.gender},null)
        </foreach>
    </insert>
​
<!--批量删除:void deleteMoreEmp(@Param("empIds") Integer[] empIds);-->
    <delete id="deleteMoreEmp">
        delete from t_emp where 
        <foreach collection="empIds" item="empId" separator="or">
            <!--#{empId}:这里面写的是item属性的值,他代表数组中的每一项 -->
            emp_id = #{empId}
        </foreach>
    </delete>
    <delete id="deleteMoreEmpOne">
        delete from t_emp where emp_id in
        <foreach collection="empIds" item="empId" separator="," open="(" close=")">
            <!--#{empId}:这里面写的是item属性的值,他代表数组中的每一项 -->
            #{empId}
        </foreach>
    </delete>

Test class:

//批量添加
@Test
public void testInsertMoreEmp(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
    Emp emp1 = new Emp(null, "小明1", 20, "男");
    Emp emp2 = new Emp(null, "小明2", 20, "男");
    Emp emp3 = new Emp(null, "小明3", 20, "男");
    List<Emp> emps = Arrays.asList(emp1, emp2, emp3);
    mapper.insertMoreEmp(emps);
}
​
​
//批量删除
@Test
public void testDeleteMoreEmp(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
    Integer[] ids = new Integer[]{6,7};
    mapper.deleteMoreEmp(ids);
}

9.6, SQL fragments

sql fragment, which can record a public sql fragment, and import it through the include tag where it is used

<sql id="empColumns">
    eid,ename,age,sex,did
</sql>
select <include refid="empColumns"></include> from t_emp

10. MyBatis cache

10.1, MyBatis's first-level cache

The first-level cache is at the SqlSession level. The data queried through the same SqlSession will be cached. The next time the same data is queried, it will be directly obtained from the cache and will not be re-accessed from the database.

Four situations that invalidate the first-level cache:

1) Different SqlSessions correspond to different first-level caches

2) The same SqlSession but different query conditions

3) Any addition, deletion, and modification operations are performed during the two queries of the same SqlSession

4) The cache is manually cleared during two queries of the same SqlSession

//一级缓存
@Test
public void testGetEmpById(){
    SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
    CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
​
    Emp empById1 = mapper1.getEmpById(1);
    System.out.println(empById1);
    //sqlSession1.clearCache();清理缓存
​
    Emp empById2 = mapper1.getEmpById(1);
    System.out.println(empById2);

10.2. Second level cache of MyBatis

The second-level cache is at the SqlSessionFactory level, and the results of SqlSession queries created through the same SqlSessionFactory will be

Cache; if the same query statement is executed again later, the result will be obtained from the cache

Conditions for enabling the secondary cache:

a>In the core configuration file, set the global configuration attribute cacheEnabled="true", the default is true, no need to set

b> Set the tag <cache/> in the mapping file

c> The second-level cache must be valid after the SqlSession is closed or submitted sqlSession1.close();

d> The entity class type converted by the queried data must implement the serialized interface implements Serializable

Circumstances that invalidate the second-level cache:

Any addition, deletion, or modification between two queries will invalidate the first-level and second-level caches at the same time

//二级缓存
@Test
public void testCache() throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
​
    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
    CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
    Emp empById1 = mapper1.getEmpById(1);
    System.out.println(empById1);
    sqlSession1.close();
​
    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
    CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
    Emp empById2 = mapper2.getEmpById(1);
    System.out.println(empById2);
    sqlSession1.close();
}

10.3, related configuration of the second level cache

The cache tag added in the mapper configuration file can set some properties:

①eviction attribute: Cache recycling strategy, the default is LRU.

LRU (Least Recently Used) – Least Recently Used: Remove objects that have not been used for the longest time.

FIFO (First in First out) - first in, first out: objects are removed in the order they entered the cache.

SOFT - Soft References: Remove objects based on garbage collector state and soft reference rules.

WEAK - Weak References: More aggressively remove objects based on garbage collector state and weak reference rules.

②flushInterval attribute: refresh interval, in milliseconds

The default is not set, that is, there is no refresh interval, and the cache is only refreshed when the statement is called

③size ​​attribute: number of references, positive integer

Represents how many objects the cache can store at most, too large will easily lead to memory overflow

④readOnly attribute: read-only, true/false

true: read-only cache; returns the same instance of the cache object to all callers. Therefore these objects cannot be modified. This provides a significant performance advantage.

false: read-write cache; a copy of the cached object will be returned (via serialization). This will be slower, but safe, so the default is

false。

10.4. The order of MyBatis cache query

Query the second-level cache first, because there may be data that other programs have checked out in the second-level cache, which can be used directly.

If the second-level cache does not hit, then query the first-level cache

If there is no hit in the first level cache, query the database

After the SqlSession is closed, the data in the first level cache will be written to the second level cache

10.5. Integrate third-party cache EHCache

10.5.1. Add dependencies

<!-- Mybatis EHCache整合包 -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

10.5.2. Functions of each jar package

jar package name effect
mybatis-ehcache Integration package of Mybatis and EHCache
ehcache EHCache core package
slf4j-api SLF4J log portal package
logback-classic Supports a concrete implementation of the SLF4J facade interface

10.5.3. Create EHCache configuration file ehcache.xml

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\atguigu\ehcache"/>
    <defaultCache
        maxElementsInMemory="1000"
        maxElementsOnDisk="10000000"
        eternal="false"
        overflowToDisk="true"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

10.5.4. Set the type of L2 cache

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

10.5.5, add logback log

When SLF4J exists, log4j as a simple log will be invalid. At this time, we need to use the specific implementation of SLF4J logback to print the log. Create the logback configuration file logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是: 时间、日志级别、线程名称、打印日志的类、日志主体内容、换行-->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger][%msg]%n</pattern>
        </encoder>
    </appender>
    <!-- 设置全局日志级别。日志级别按顺序分别是: DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT" />
    </root>
    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="com.yka.mybatis.mapper" level="DEBUG"/>
</configuration>

10.5.6, EHCache configuration file description

attribute name Is it necessary effect
maxElementsInMemory yes The maximum number of elements cached in memory
maxElementsOnDisk yes The maximum number of elements cached on disk, if 0 means infinity
eternal yes Sets whether cached elements should never expire. If it is true, the cached data is always valid. If it is false, it must be judged according to timeToIdleSeconds and timeToLiveSeconds
overflowToDisk yes Set whether to cache expired elements to disk when the memory cache overflows
timeToIdleSeconds no When the data cached in EhCache is accessed twice before and after the time exceeds the value of the timeToIdleSeconds attribute, the data will be deleted. The default value is 0, which means that the idle time is infinite
timeToLiveSeconds no The effective lifetime of the cache element, the default is 0, that is, the element survival time is infinite
diskSpoolBufferSizeMB no DiskStore (disk cache) buffer size. The default is 30MB. Each Cache should have its own buffer
diskPersistent no Whether to enable the disk to save the data in EhCache when the VM is restarted, the default is false.
diskExpiryThreadIntervalSeconds no The running interval of the disk cache cleaning thread, the default is 120 seconds. Every 120s, the corresponding thread will clean up the data in EhCache once
memoryStoreEvictionPolicy no When the memory cache reaches the maximum and a new element is added, the policy to remove the element in the cache. The default is LRU (least recently used), optional LFU (least frequently used) and FIFO (first in first out)

11. Reverse engineering of MyBatis

Forward engineering: Create Java entity classes first, and the framework is responsible for generating database tables based on entity classes. Hibernate supports forward engineering.

Reverse engineering: first create the database table, and the framework is responsible for reversely generating the following resources according to the database table:

  • Java entity class

  • Mapper interface

  • Mapper mapping file

11.1. Steps to create reverse engineering

①Add dependencies and plug-ins

<!-- 依赖MyBatis核心包 -->
<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
    <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- log4j日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
    <!-- 构建过程中用到的插件 -->
    <plugins>
        <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.0</version>
            <!-- 插件的依赖 -->
            <dependencies>
                <!-- 逆向工程的核心依赖 -->
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.2</version>
                </dependency>
                <!-- MySQL驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.16</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

②Create the core configuration file of MyBatis

③Create a configuration file for reverse engineering

The file name must be: 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>
    <!--
            targetRuntime: 执行生成的逆向工程的版本
                MyBatis3Simple: 生成基本的CRUD(清新简洁版)
                MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.yka.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.yka.mybatis.mapper" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.yka.mybatis.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

④ Execute the generate target of the MBG plug-in

⑤Effect

11.2. QBC query

@Test
    public void testMBG(){
​
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        //根据id查询数据
        /*Emp emp = mapper.selectByPrimaryKey(1);
        System.out.println(emp);*/
        //查询所有数据 条件为null就是查询所有
        /*List<Emp> list = mapper.selectByExample(null);
        list.forEach(System.out::println);*/
        //根据条件查询数据
        /*EmpExample example = new EmpExample();
        example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);
        example.or().andGenderEqualTo("男");
        List<Emp> list = mapper.selectByExample(example);
        list.forEach(System.out::println);*/
        Emp emp = new Emp(1, "小黑", null, "女");
        //测试普通修改功能其中一个属性为null 该字段也会更改
        //mapper.updateByPrimaryKey(emp);
        //测试选择性修改,其中一个属性为null 该字段不会更改
        mapper.updateByPrimaryKeySelective(emp);
​
    }

12. Pagination plug-in

limit index,pageSize

pageSize: the number of items displayed on each page

pageNum: the page number of the current page

index: the starting index of the current page, index=(pageNum-1)*pageSize

count: the total number of records

totalPage: the total number of pages

totalPage = count / pageSize;

if(count % pageSize != 0){

totalPage += 1;

}

pageSize=4,pageNum=1,index=0 limit 0,4

pageSize=4,pageNum=3,index=8 limit 8,4

pageSize=4,pageNum=6,index=20 limit 8,4

Home Prev 2 3 4 5 6 Next Last

12.1. Steps to use the pagination plug-in

① Add dependencies

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

② Configure the pagination plug-in

Configure the plugin in the MyBatis core configuration file

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>  

test class

@Test
public void testPage(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    //查询功能之前开启分页功能
    Page<Object> page = PageHelper.startPage(5, 4);
    List<Emp> list = mapper.selectByExample(null);
    //查询功能之后可以获取分页相关的所有数据
    PageInfo<Emp> pageInfo = new PageInfo<>(list, 5);
    list.forEach(System.out::println);
    System.out.println(pageInfo);
}

12.2, the use of pagination plug-ins

a> Use PageHelper.startPage(int pageNum, int pageSize) to enable the paging function before the query function

pageNum: the page number of the current page

pageSize: the number of items displayed on each page

b> After querying to obtain the list collection, use PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages) to obtain paging related data

list: data after pagination

navigatePages: the number of pages in the navigation page

c> Pagination related data

PageInfo{

pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,

list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,

pages=8, reasonable=false, pageSizeZero=false},

prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,

hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,

navigatepageNums=[4, 5, 6, 7, 8]

}

pageNum: the page number of the current page

pageSize: the number of items displayed on each page

size: the actual number of items displayed on the current page

total: the total number of records

pages: total number of pages

prePage: the page number of the previous page

nextPage: the page number of the next page

isFirstPage/isLastPage: whether it is the first page/last page

hasPreviousPage/hasNextPage: Whether there is a previous page/next page

navigatePages: the number of pages in the navigation page

navigatepageNums: the page number of the navigation page, [1,2,3,4,5]

PageInfo{

pageNum=2, pageSize=4, size=4, startRow=5, endRow=8, total=56, pages=14,

list=Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=56, pages=14, reasonable=false, pageSizeZero=false}

[Student{stuId=10, stuName='a', age=1, gender='1', email='null'}, Student{stuId=11, stuName='a', age=null, gender='null', email='null'}, Student{stuId=12, stuName='a', age=null, gender='null', email='null'}, Student{stuId=13, stuName='a', age=null, gender='null', email='null'}],

prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=5, navigatepageNums=[1, 2, 3, 4, 5]

}

PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)

pageInfo.list:打印后是:[Student{stuId=10, stuName='a', age=1, gender='1', email='null'}, Student{stuId=11, stuName='a', age=null, gender='null', email='null'}, Student{stuId=12, stuName='a', age=null, gender='null', email='null'}, Student{stuId=13, stuName='a', age=null, gender='null', email='null'}],

Guess you like

Origin blog.csdn.net/m0_65992672/article/details/130559309