"Mybatis Usage" - just for use, one article is enough

Table of contents

Quick start series 

Overview What is mybatis? What are the characteristics?

Preliminary work

1. Introduce log4j 

1.1, why should log4j be introduced?

​Edit 1.2, how to introduce log4j

1.2.1, import log4j dependencies

1.2.2, detailed explanation of creating log4j.properties configuration file (do not change the file name)

1.2.3, introduced in the mybatis global configuration file

2. Input and output of mybatis 

2.1, SQL parameter passing of mybatis

2.1.1, #{} (most commonly used, helps prevent sql injection)

2.1.2, ${} (unsafe)

2.2, mybatis input 

2.2.1, single parameter

2.2.2, Entity type parameters 

2.2.3, two or more simple types of data

2.2.4, Map type data input

Summarize

2.3, mybatis input

2.3.1, return a single simple type data

2.3.2, return entity type

2.3.3, return Map type

2.3.4, return List data

2.3.5, return to the auto-incrementing primary key

2.3.6, Correspondence between database table fields and entity class attributes

Summarize

3. Use Mybatis to map associations

3.1. Explanation of the concept of association relationship

3.1.1, Quantitative relationship

3.1.2, direction of association relationship

3.2, Modeling

3.2.1, order class

3.2.2, customer class

3.2, one to one

3.2.1, create orderMapper interface

3.2.2, create orderMapper configuration file 

3.3, to many

3.3.1, create customerMapper

3.3.2, create customerMapper configuration file

3.4, step-by-step query

3.4.1, Concept

3.4.2, write sql file

3.4.3. Correspondence between the elements of the configuration file

3.5, delayed query 

3.5.1, Concept

3.5.2, lower version

3.5.3, later version

4. Dynamic SQL 

4.1, if sum where

4.2,set

4.3,trim

4.4,choose/when/otherwise

4.5,foreach

 5. Introduction to caching (since this is a usage chapter, I won’t introduce it in detail here)

6. Reverse engineering 

6.1, Introduction

6.2, configure pom

 6.3, MEG configuration file

6.4, execute MBG plug-in

6.5, QBC query 

6.5.1, version selection

6.5.2, QBC query 

6.6, paging plug-in 

6.6.1, introduce dependencies in the pom file

6.6.2, use 

at last



Quick start series 

https://blog.csdn.net/weixin_64972949/article/details/130945007?spm=1001.2014.3001.5502

Overview
What is mybatis? What are the characteristics?

It is a semi-automatic ORM persistence layer framework with high SQL flexibility and supports advanced mapping (one-to-one, one-to-many), dynamic SQL, delayed loading and caching, etc., but its database independence is low

What is an ORM?

Object Relation Mapping, object relational mapping. The object refers to the Java object, and the relationship refers to the relational model in the database. Object-relational mapping refers to establishing a correspondence between the Java object and the relational model of the database, such as using a Java Student class to correspond. There is a student table in the database, and the attributes in the class correspond to the columns in the table. The Student class corresponds to the student table, and a Student object corresponds to a row of data in the student table.

Why is mybatis a semi-automatic ORM framework?

Developing with mybatis requires manually writing SQL statements. Fully automatic ORM frameworks, such as hibernate, do not require writing SQL statements. When developing with hibernate, you only need to define the ORM mapping relationship, and you can directly perform CRUD operations. Because mybatis requires hand-written SQL statements, it has high flexibility. You can freely customize the SQL as needed. Also because it requires hand-written SQL, when you want to switch databases, the SQL statements may have to be rewritten because of different The database has different dialects (Dialect), so the database of mybatis has low independence. Although mybatis requires hand-written SQL, compared with JDBC, it provides input mapping and output mapping, which can easily set SQL parameters and encapsulate result sets. It also provides functions such as related query and dynamic SQL, which greatly improves the efficiency of development. And its learning cost is much lower than hibernate

Preliminary work

First, prepare three tables, namely emp3, t_customer, and t_order.

emp3:

DROP TABLE IF EXISTS `emp3`;
CREATE TABLE `emp3`  (
  `emp_id` int(0) NOT NULL AUTO_INCREMENT,
  `wkname` varchar(10) CHARACTER SET utf32 COLLATE utf32_general_ci NULL DEFAULT NULL,
  `address` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'Unknown',
  `job_id` int(0) NULL DEFAULT 0,
  PRIMARY KEY (`emp_id`) USING BTREE,
  INDEX `emp_name_index`(`wkname`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
 
-- ----------------------------
-- Records of emp3
-- ----------------------------
INSERT INTO `emp3` VALUES (1, '张三', '衡阳', 12);
INSERT INTO `emp3` VALUES (2, '李四', '郴州', 13);
INSERT INTO `emp3` VALUES (3, '王五', '岳阳', 14);
 
SET FOREIGN_KEY_CHECKS = 1;

t_order&t_customer 

CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );

CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) ); 

INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');

INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1'); 

1. Introduce log4j 

1.1, why should log4j be introduced?

First of all, why should log4j be introduced? Let me show you an example first. In the mybatis global configuration file, I will introduce log4j

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

Comment it out first.

Then, if we randomly execute one of our test methods, we will find that the console prints a bunch of things that we cannot understand (this is because I introduced SLF4J and logback log display before, these two things are helpful for us to know Where is the error reported? If it is not introduced, the information will be less). 

After that, we uncomment and execute the same test method again. We will find that there is a lot of useful information for us on the console. The most important thing is that it can display the SQL statement you are currently executing and the parameters you passed in. These two pieces of information are very useful when we debug methods. Therefore, it is very necessary for us to introduce log4j logs.

 1.2, how to introduce log4j

1.2.1, import log4j dependencies

            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>

1.2.2, detailed explanation of creating log4j.properties configuration file (do not change the file name)

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/fanlan.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

1.2.3, introduced in the mybatis global configuration file

     <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

 Note: The order of configuration information in the global configuration file is fixed and cannot be changed at will. As shown below, follow the order of this picture to write our configuration information. Otherwise, an error will be reported.

You can refer to my global configuration file. Note: Here I imported the external jdbc.properties configuration file. As for the properties configuration file, I won’t go into details.

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

<configuration>
    <properties resource="jdbc.properties"/>

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
<!--    Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.-->
<!--    Cause: org.xml.sax.SAXParseException; lineNumber: 58; columnNumber: 17; -->
<!--    元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,-->
<!--    typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,-->
<!--    environments?,databaseIdProvider?,mappers?)"。-->
<!--    文件摆放的顺序是固定的,不可以改变-->

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">

        <!-- environment表示配置Mybatis的一个具体的环境,id:表示连接数据库环境的唯一标识,不可以重复 -->
        <environment id="development">

            <!-- Mybatis的内置的事务管理器 -->
<!--            type: JDBC/MANAGED-->
<!--            JDBC:表示当前环境中,执行SQL时,使用的是JDBC原生的事务管理方式,事务的提交和回滚是手动的-->
<!--            MANAGED:被管理,例如spring-->
            <transactionManager type="JDBC"/>

            <!-- 配置数据源 -->
            <dataSource type="POOLED">
<!--                type:设置数据源的类型-->
<!--                type=POOLED|UNPOOLED|JNDI-->
<!--                POOLED:表示使用数据库连接池缓存数据库连接-->
<!--                UNPOOLED:不使用数据库连接池-->
<!--                JNDI:表示使用上下文中的数据源-->
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${user}"/>
                <property name="password" value="${password}"/>
            </dataSource>

        </environment>
    </environments>



    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="mappers/EmpolyeeMapper.xml"/>
        <!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
        <mapper resource="mappers/OrderMapper.xml"/>

        <mapper resource="mappers/CustomerMapper.xml"/>
    </mappers>


</configuration>

The project directory structure is as follows:

  

2. Input and output of mybatis 

2.1, SQL parameter passing of mybatis

2.1.1, #{} (most commonly used, helps prevent sql injection)

First, call the query method to query the employees with empId=1. 

The corresponding sql statement.

    <select id="selectEmpolyee" resultType="com.zhuzhu.Empolyee">
        <!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->
        select emp_id empId,wkname name,address address,job_id jobId from emp3 where emp_id=#{empId}
    </select>

It can be seen that #{} in mybatis will eventually be parsed as ?, which is actually the ? placeholder in Jdbc's PreparedStatement. It has a pre-compilation process and will perform type analysis on the input parameters (if the input parameter is String type, quotes will be added automatically when setting parameters), which can prevent SQL injection. If the input parameter type specified by the parameterType attribute is a simple type (simple type refers to the 8 java primitive types plus a String), #{} The variable name can be arbitrary.

2.1.2, ${} (unsafe)

Change the above sql statement to.

 <select id="selectEmpolyee" resultType="com.zhuzhu.Empolyee">
        <!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->
        select emp_id empId,wkname name,address address,job_id jobId from emp3 where emp_id=#{empId}
    </select>

Then, after executing the same method, we found that what ${} does for us at the bottom is string splicing. Those who are familiar with SQL injection know that string splicing is a big pitfall.

2.2, mybatis input 

2.2.1, single parameter

  int deleteEmpolyeeById(Integer empId);

Corresponds to sql

    <delete id="deleteEmpolyeeById">
        delete from emp3 where emp_id=#{empId}
    </delete>

When there is only one parameter, just pass it directly, but note: write the field name in the sql statement and the corresponding attribute name in #{}

A single simple type parameter can be named arbitrarily in #{}, but it is not necessary. Usually the same name as the interface method parameter is used.

2.2.2, Entity type parameters 

 int updateEmpolyeeById(Empolyee empolyee);

Corresponds to sql

<update id="updateEmpolyeeById">
        update emp3 set wkname=#{wkname},address=#{address}
        where emp_id=#{empId}
    </update>

Mybatis will process the getXxx() method based on the data passed in #{}, and call this method in the entity class object through reflection to obtain the corresponding data. Fill in the position of #{}parsed question mark placeholder. Therefore, when using entity type data, the name in #{} cannot be customized, otherwise the value cannot be obtained through the getXxx() method.

2.2.3, two or more simple types of data

int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);

sql

<update id="updateEmployee">

  update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}

</update>

 If there are multiple scattered simple type parameters, Mybatis cannot recognize the custom name without special processing, so we need to use the @Param annotation to mark it.

2.2.4, Map type data input

int updateEmployeeByMap(Map<String, Object> paramMap);

sql

<update id="updateEmployeeByMap">

  update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}

</update>

 #{}Write the Key value in the Map to extract the data and put it into the sql statement.

Summarize

1. For a single simple type parameter, we can customize its name in #{}, but usually the same name as the interface method parameter is used.

2. For entity type parameters, Mybatis will process the getXxx() method based on the data passed in #{}, and call this method in the entity class object through reflection to obtain the corresponding data. Fill in the position of #{}parsed question mark placeholder

3. For more than two simple parameters, we need to use @Param annotation, otherwise mybatis will not recognize it.

4. For Map type data, write the key in the Map in #{}

2.3, mybatis input

2.3.1, return a single simple type data

int selectEmpCount();

sql

<select id="selectEmpCount" resultType="int">

  select count(*) from t_emp

</select>

 Mybatis internally sets many aliases for commonly used data types. Taking the int type as an example, the names that can be written are: int, integer, Integer, java.lang.Integer, Int, INT, INTEGER, etc.

2.3.2, return entity type

Empolyee selectEmpolyee(Integer empId);

sql

<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --> 
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
 <select id="selectEmpolyee" resultType="com.zhuzhu.Empolyee"> 
<!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->                     
      select emp_id empId,wkname name,address address,job_id jobId 
      from emp3 where emp_id=${empId}
 </select>

When returning the entity type, we only need to set the resultType to the full class name of the corresponding entity class.

2.3.3, return Map type

Map<String,Object> selectEmpNameAndMaxJobId();

sql

   <select id="selectEmpNameAndMaxJobId" resultType="map">
        SELECT
            wkname ,
            job_id ,
            (SELECT AVG(job_id) FROM emp3)
        FROM emp3 WHERE job_id=(
            SELECT MAX(job_id) FROM emp3
        )
    </select>

As shown in the figure below, through the log file generated by the following operation, we can know that the Key value of the returned Map data is the field name, and the Value value is the queried content, for example ("job_id", "16").

2.3.4, return List data

The fields returned by SQL queries do not correspond to any existing entity class and cannot be encapsulated into entity class objects. If it can be encapsulated into an entity class type, the Map type is not used.

List<Empolyee> selectAll();

sql

<select id="selectAll" resultType="com.zhuzhu.Empolyee">
    select emp_id empId,wkname name,address address,job_id jobId from emp3 
</select>

The resultType here is still the full class name of the elements in our List collection.

2.3.5, return to the auto-incrementing primary key

Usually we will set the primary key id of the database table to auto-increment. When inserting a record, we do not set its primary key id, but let the database automatically generate the primary key id of the record. So after inserting a record, how do we get the primary key id of the record automatically generated by the database? There are two ways

1. Use useGeneratedKeys and keyProperty attributes (requires the database to support auto-incrementing primary keys, such as Mysql)

<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">

  insert into t_emp(emp_name,emp_salary)
  values(#{empName},#{empSalary})

</insert>

Note: Mybatis sets the value of the auto-incrementing primary key into the entity class object, rather than returning it in the form of the return value of the Mapper interface method.

2. Use subtags (for databases that do not support auto-incrementing primary keys, such as Oracle)

<insert id="insertEmployee" parameterType="com.atguigu.mybatis.beans.Employee"  databaseId="oracle">

  <selectKey order="BEFORE" keyProperty="id" resultType="integer">

    select employee_seq.nextval from dual 

  </selectKey>  

  insert into orcl_employee(id,last_name,email,gender) values(#{id},#{lastName},#{email},#{gender})

</insert>

2.3.6, Correspondence between database table fields and entity class attributes

1. Alias

Set the field alias to be consistent with the entity class attribute.

For example, when we write a select statement, we write the attribute name of the entity class after the field name.

<select id="selectAll" resultType="com.zhuzhu.Empolyee"> 
    select emp_id empId,wkname name,address address,job_id jobId from emp3 
</select>

2. Global configuration automatically recognizes camel case naming rules

Add the following configuration to the Mybatis global configuration file:

Note: When adding, you still need to pay attention to the order of writing.

<!-- 使用settings对Mybatis全局进行设置 --> 
<settings> 
    <!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 --> 
    <setting name="mapUnderscoreToCamelCase" value="true"/> 
</settings>

If you add this configuration, you no longer need to write aliases in your select statement, but the premise is that both your attribute names and the field names in the database need to comply with the naming rules:

For example, in the database, the field name is emp_id, and the attribute name is empId. In fact, just capitalize the letter after the underscore.

3. Use resultMap

Use the resultMap tag to define the corresponding relationship, and then reference this corresponding relationship in subsequent SQL statements.

 <resultMap id="selectEmployeeByRMResultMap" type="com.zhuzhu.Empolyee">

        <!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
        <!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
        <id column="emp_id" property="empId"/>

        <!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
        <result column="wkname" property="wkname"/>

        <result column="address" property="address"/>

    </resultMap>

    <!-- Employee selectEmployeeByRM(Integer empId); -->
    <select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">

        select emp_id,wkname,address from emp3 where emp_id=#{empId}

    </select>

Summarize

1. When returning simple type data, such as int data type, you can write the names in resultType: int, integer, Integer, java.lang.Integer, Int, INT, INTEGER.

2. When returning entity type data, directly write the full class name of the entity type in the resultType.

3. When returning Map type data, the Key value of the returned Map data is the field name, and the Value value is the queried content, for example ("job_id", "16").

4. When returning List data, resultType still writes the full class name of the elements in our List collection.

5. When returning the auto-increment primary key, there are two ways. One is to use useGeneratedKeys and keyProperty attributes for databases that can auto-increment primary keys - Mysql, and the other is to use the selectKey tag for databases that cannot auto-increment primary keys - Oracle.

6. There are three ways to correspond to database table fields and entity class attributes - 1. Alias, 2. Automatic matching, 3. Use resltMap for binding. You only need to implement one of the methods.

3. Use Mybatis to map associations

3.1. Explanation of the concept of association relationship

3.1.1, Quantitative relationship

Mainly reflected in database tables

One to one

    Husband and wife relationship, person and ID number,
one to many

    User and user orders, locks and keys

 many to many

    Teachers and students, departments and staff

3.1.2, direction of association relationship

Mainly reflected in Java entity classes

- Two-way: both parties can access each other
    - Customer: the collection attribute containing Order
    - Order: containing the attributes of a single Customer
- One-way: only one of the two parties can access the other
    - Customer: the collection attribute not containing Order, cannot be accessed Order
    - Order: Contains properties for a single Customer

3.2, Modeling

3.2.1, order class

package com.zhuzhu;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {

    private Integer orderId;

    private String orderName;

    private Customer customer;// 体现的是对一的关系
}

3.2.2, customer class

package com.zhuzhu;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {

    private Integer customerId;

    private String customerName;

    private List<Order> orderList;// 体现的是对多的关系
}

Note: I have already created the table before. 

3.2, one to one

3.2.1, create orderMapper interface

package com.zhuzhu.Dao;

import com.zhuzhu.Order;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper {
    Order selectOrderWithCustomer(Integer orderId);
}

3.2.2, create orderMapper configuration 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">

<!-- mapper是根标签,namespace属性:在Mybatis全局范围内找到一个具体的Mapper配置 -->
<!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 -->
<mapper namespace="com.zhuzhu.Dao.OrderMapper">

    <!-- 创建resultMap实现“对一”关联关系映射 -->
    <!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上“ResultMap” -->
    <!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
    <resultMap id="selectOrderWithCustomerResultMap" type="com.zhuzhu.Order">

        <!-- 先设置Order自身属性和字段的对应关系 -->
        <!-- 主键使用id标签,其他键使用result标签       -->
        <id column="order_id" property="orderId"/>

        <result column="order_name" property="orderName"/>

        <!-- 使用association标签配置“对一”关联关系 -->
        <!-- property属性:在Order类中对一的一端进行引用时使用的属性名 -->
        <!-- javaType属性:一的一端类的全类名 -->
        <association property="customer" javaType="com.zhuzhu.Customer">

            <!-- 配置Customer类的属性和字段名之间的对应关系 -->
            <id column="customer_id" property="customerId"/>
            <result column="customer_name" property="customerName"/>

        </association>

    </resultMap>

    <!-- Order selectOrderWithCustomer(Integer orderId); -->
    <select id="selectOrderWithCustomer" resultMap="selectOrderWithCustomerResultMap">

        SELECT order_id,order_name,c.customer_id,customer_name
        FROM t_order o
                 LEFT JOIN t_customer c
                           ON o.customer_id=c.customer_id
        WHERE o.order_id=#{orderId}

    </select>
</mapper>

Here, we use the association tag to bind the one-to-one relationship between order and customer.

Use the association tag to configure the "to-one" association
property attribute: the attribute name used when referencing one end of one in the Order class
 javaType attribute: the full class name of the one end class

3.3, to many

3.3.1, create customerMapper

package com.zhuzhu.Dao;

import com.zhuzhu.Customer;

public interface CustomerMapper {
    Customer selectCustomerWithOrderList(Integer customerId);

}

3.3.2, create customerMapper configuration file

<!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 -->
<resultMap id="selectCustomerWithOrderListResultMap"

  type="com.atguigu.mybatis.entity.Customer">

  <!-- 映射Customer本身的属性 -->
  <id column="customer_id" property="customerId"/>

  <result column="customer_name" property="customerName"/>

  <!-- collection标签:映射“对多”的关联关系 -->
  <!-- property属性:在Customer类中,关联“多”的一端的属性名 -->
  <!-- ofType属性:集合属性中元素的类型 -->
  <collection property="orderList" ofType="com.zhuzhu.Order">

    <!-- 映射Order的属性 -->
    <id column="order_id" property="orderId"/>

    <result column="order_name" property="orderName"/>

  </collection>

</resultMap>

<!-- Customer selectCustomerWithOrderList(Integer customerId); -->
<select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">

  SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
  FROM t_customer c
  LEFT JOIN t_order o
  ON c.customer_id=o.customer_id
  WHERE c.customer_id=#{customerId}

</select>

Collection tag: Mapping the "to-many" association
property attribute: In the Customer class, the attribute name of the end of the association "many" 
ofType attribute: The type of the element in the collection attribute

3.4, step-by-step query

3.4.1, Concept

In order to achieve lazy loading, the query for Customer and Order must be separated and divided into two steps before it can be realized. To do this, we need to query the Order separately, that is, we need to write a separate SQL statement to query the Order collection data in the Mapper configuration file.

3.4.2, write sql file

Implement step-by-step query in the customerMapper configuration file. The configuration file is as follows:

<?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属性:在Mybatis全局范围内找到一个具体的Mapper配置 -->
<!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 -->
<mapper namespace="com.zhuzhu.Dao.CustomerMapper">

    <!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 -->
    <resultMap id="selectCustomerWithOrderListResultMap"

               type="com.zhuzhu.Customer">

        <!-- 映射Customer本身的属性 -->
        <id column="customer_id" property="customerId"/>

        <result column="customer_name" property="customerName"/>

       
<!--        分步查询-->
        <!-- orderList集合属性的映射关系,使用分步查询 -->
        <!-- 在collection标签中使用select属性指定要引用的SQL语句 -->
        <!-- select属性值的格式是:Mapper配置文件的名称空间.SQL语句id -->
        <!-- column属性:指定Customer和Order之间建立关联关系时所依赖的字段 -->
        <collection

                property="orderList"

                select="com.zhuzhu.Dao.CustomerMapper.selectOrderList"

                column="customer_id"/>

    </resultMap>
    <select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">

        select customer_id,customer_name from t_customer
        where customer_id=#{customerId}

    </select>
<!--    注意select语句要是没有resultmap去指定字段对应属性,
就需要在select语句中起别名去对应字段order_id orderId,order_name orderName-->
    <select id="selectOrderList" resultType="com.zhuzhu.Order">

        select order_id orderId,order_name orderName from t_order where customer_id=#{customer_id}

    </select>
</mapper>

3.4.3. Correspondence between the elements of the configuration file

 

3.5, delayed query 

3.5.1, Concept

When querying Customer, the Order's List collection data may not be used. If the Order's collection data is never used, the memory occupied by this part of the data is wasted. In this regard, we hope that data that may not necessarily be used can be queried when needed.  

For example: among 1,000 queries on Customer, only 15 of them will use the Order collection data. Then querying only when needed can greatly save memory space.  

The concept of lazy loading: The properties associated with the entity class are not queried until they are needed. Also called lazy loading.

3.5.2, lower version

<!-- 使用settings对Mybatis全局进行设置 -->
<settings>

  <!-- 开启延迟加载功能:需要配置两个配置项 -->
  <!-- 1、将lazyLoadingEnabled设置为true,开启懒加载功能 -->
  <setting name="lazyLoadingEnabled" value="true"/>

  <!-- 2、将aggressiveLazyLoading设置为false,关闭“积极的懒加载” -->
  <setting name="aggressiveLazyLoading" value="false"/>

</settings>

3.5.3, later version

<!-- Mybatis全局配置 -->
<settings>

  <!-- 开启延迟加载功能 -->
  <setting name="lazyLoadingEnabled" value="true"/>

</settings>

4. Dynamic SQL 

4.1, if sum where

    <select id="selectEmpolyeeByCondition" resultType="com.zhuzhu.Empolyee">
        select emp_id empId,wkname wkname,address address,job_id jobId from emp3
        <!-- where标签会自动去掉“标签体内前面多余的and/or” -->
        <where>
            <!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
            <!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
            <if test="wkname!=null and wkname!=''">
                and job_id=#{jobId}
            </if>
<!--            <if test="jobId=null">-->
<!--                or address=#{address}-->
<!--            </if>-->
        </where>
    </select>

4.2,set

    <update id="updateEmpolyeeDynamic">
        update emp3
        <set>
            <if test="wkname!=null and wkname!=''">
<!--             注意这里后面有个”,“逗号-->
                wkname=#{wkname},
            </if>
<!--            <if test="address=null">-->
<!--                address="未知",-->
<!--            </if>-->
        </set>
        where job_id=#{jobId}
    </update>

4.3,trim

<select id="selectEmpolyeeByConditionByTrim" resultType="com.zhuzhu.Empolyee">
        select emp_id empId,wkname wkname,address address,job_id jobId from emp3
        <!-- prefix属性指定要动态添加的前缀 -->
        <!-- suffix属性指定要动态添加的后缀 -->
        <!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
        <!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
        <!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
        <trim prefix="where" suffixOverrides="and|or">
            <if test="jobId &gt; 10">
                job_id>#{jobId} and
            </if>
<!--            <if test="empAge &lt;= 20">-->
<!--                emp_age=#{empAge} or-->
<!--            </if>-->
<!--            <if test="wkname!=null and wkname!=''">-->
<!--                wkname=#{wkname} or -->
<!--            </if>-->
        </trim>
    </select>

4.4,choose/when/otherwise

    <select id="selectEmployeeByConditionByChoose" resultType="com.zhuzhu.Empolyee">
        select emp_id empId,wkname wkname,address address,job_id jobId from emp3
        where
<!--        - 从上到下依次执行条件判断
        - 遇到的第一个满足条件的分支会被采纳
        - 被采纳分支后面的分支都将不被考虑
        - 如果所有的when分支都不满足,那么就执行otherwise分支 -->
        <choose>
            <when test="wkname!=null and wkname!=''"> job_id=#{jobId}</when>
            <otherwise>1=1</otherwise>
        </choose>
    </select>

4.5,foreach

Collection attribute: The collection to be traversed.
    Item attribute: During the process of traversing the collection, each specific object can be obtained. Set a name in the item attribute. The traversed object will be referenced through this name in the future. Separator attribute: Specifies when the tag
    body of the foreach tag is repeated. When splicing strings, the separator between each tag body string
    open attribute: specifies the string to be added in front of the entire string after the entire loop is assembled.
    close attribute: specifies the entire loop after the string is assembled. , the string index attribute to be added after the entire string
    : give a name here to facilitate subsequent reference when traversing
        the List collection. Here you can get the index value of the List collection
        . When traversing the Map collection, you can get the key of the Map collection.

    <insert id="insertEmployeeByConditionByForeach">
        insert into emp3 value 
        <foreach collection="emps" item="emp" separator=",">
            (#{emp.empId},#{emp.wkname},#{emp.address},#{emp.jobId})
        </foreach>
    </insert>

Test code:

    @Test
    public void TestForeach(){
        List<Empolyee> list = new ArrayList<>();
        list.add(new Empolyee(10,"厦门","张三丰",15));
        list.add(new Empolyee(11,"深圳","张无忌",16));
        EmpolyeeMapper empolyeeMapper = session.getMapper(EmpolyeeMapper.class);
        int i = empolyeeMapper.insertEmployeeByConditionByForeach(list);
    }

 As you can see, the essence of batch insert is actually to execute a SQL statement .

Batch update. To achieve batch update, you need to put together multiple SQL statements and separate them with semicolons. That is, multiple SQL statements are sent at once for the database to execute. At this time, you need to set the URL address of the database connection information: 

url=jdbc:mysql://localhost:3306/h3?allowMultiQueries=true

 5. Introduction to caching (since this is a usage chapter, I won’t introduce it in detail here)

L1 cache

Enabled by default, the same SqlSesion level shared cache executes the same SQL query twice within the life cycle of a SqlSession. The second SQL query will directly fetch the cached data without going to the database. Of course, if the first If DML (INSERT/UPDATE/DELETE) is executed between the first and second identical SQL queries, the first-level cache will be cleared, and the second query of the same SQL will still go to the database.

The first level cache will be cleared in the following situations

When adding, deleting, and modifying operations are performed under the same SqlSession (no need to submit), the first-level cache will be cleared.
When the SqlSession is submitted or closed (it will be automatically submitted when closed), the first-level cache will be cleared.
A certain CRUD tag in mapper.xml is set. The attribute flushCache=true will cause the MappedStatement's first-level cache and second-level cache to become invalid (a CRUD tag will be encapsulated into a MappedStatement in mybatis). Set <setting name="localCacheScope"
value=" in the global configuration file STATEMENT"/>, this will invalidate the first-level cache and the second-level cache will not be affected
.

It is turned off by default. You can turn on the secondary cache master switch through <settings name="cacheEnabled" value="true"/> in the global configuration file, and then add <cache /> to a specific mapper.xml to enable it. The second level cache of mapper.xml. The second-level cache is a mapper-level cache with a larger granularity than the first-level cache. Multiple SqlSession can share the same mapper's second-level cache. Note that after turning on the second-level cache, SqlSession needs to be submitted before the queried data will be refreshed to the second-level cache.
 

6. Reverse engineering 

6.1, Introduction

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

Reverse: Create a database table first, and the framework is responsible for reversely generating entity classes, mapper interfaces, and mapper configuration files based on the database table.

6.2, configure pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>testMybatis</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>MybatisDemo2</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- Mybatis核心 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
        </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>

                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>

                    <!-- MySQL驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.20</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

 6.3, MEG configuration file

In generatorConfig.xml (this configuration file name is agreed in this way, it is best not to change it), configure the following information;

<?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/h3?serverTimezone=UTC&amp;useSSL=false&amp;rewriteBatchedStatements=true"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.zhuzhu.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="mapper"  targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.zhuzhu.mybatis.mapper"  targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="emp3" domainObjectName="Employee"/>
<!--        <table tableName="t_customer" domainObjectName="Customer"/>-->
<!--        <table tableName="t_order" domainObjectName="Order"/>-->
    </context>
</generatorConfiguration>

 <property name="enableSubPackages" value="true" />

On the basis of targetPackage, another layer of package is generated according to the schema of the database. The final generated class is placed under this package. The default is false.

As shown below: 

6.4, execute MBG plug-in

After configuring the above information, double-click the following plug-in to reversely generate the pojo, mapper interface, and mapper.xml configuration file

6.5, QBC query 

6.5.1, version selection

At this location in the configuration file, we specify whether to use the luxury version or the simple version. The version selection has been specified before generating the pojo, mapper interface, and mapper.xml configuration file. Only after selecting the version can the reverse engineering be generated.

<!--
            targetRuntime: 执行生成的逆向工程的版本
                    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
                    MyBatis3: 生成带条件的CRUD(奢华尊享版)
     -->
    <context id="DB2Tables" targetRuntime="MyBatis3">

Luxury version: There are more query methods, and there is basically no need to write SQL by hand.

Concise version: There are not many query methods, and sometimes you need to write sql by yourself.

6.5.2, QBC query 

Under the test package, write the test class testMBG class.

package com.zhuzhu;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhuzhu.mybatis.mapper.EmployeeMapper;
import com.zhuzhu.mybatis.pojo.Employee;
import com.zhuzhu.mybatis.pojo.EmployeeExample;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;

public class TestMBG {
    private SqlSession session;
    private Logger log = LoggerFactory.getLogger(TestMBG.class);
    // junit会在每一个@Test方法前执行@Before方法
    @Before
    public void init() throws IOException {
        session = new SqlSessionFactoryBuilder()
                .build(
                        Resources.getResourceAsStream("mybatis-config.xml"))
                .openSession();
        //当openSession中的值是true的时候,就会自动提交事务
    }

    // junit会在每一个@Test方法后执行@After方法
    @After
    public void clear() {
        //由于使用的是JDBC,所以不会自动提交事务,所以这里手动提交
        session.commit();
        session.close();
    }
    @Test
    public  void testMBG(){
        EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);

        //条件查询
        EmployeeExample employeeExample1 = new EmployeeExample();
        employeeExample1.createCriteria().andAddressEqualTo("长沙");
        employeeExample1.or().andJobIdBetween(12,14);
        List<Employee> list = employeeMapper.selectByExample(employeeExample1);
        list.forEach(employee -> System.out.println(employee));
        //根据条件修改
        //下面的意思就是说,将id为7的元素,修改为Employee(7,null,null,12)
        EmployeeExample employeeExample2 = new EmployeeExample();
        employeeExample2.createCriteria().andEmpIdEqualTo(7);
        employeeMapper.updateByExampleSelective(new Employee(7,null,null,12),employeeExample);
    }
}

        EmployeeExample employeeExample1 = new EmployeeExample();
        employeeExample1.createCriteria().andAddressEqualTo("长沙");
        employeeExample1.or().andJobIdBetween(12,14); 

From here, we can know that setting employeeExample is similar to writing our sql statement, which is the condition. 

6.6, paging plug-in 

6.6.1, introduce dependencies in the pom file

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

6.6.2, use 

package com.zhuzhu;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhuzhu.mybatis.mapper.EmployeeMapper;
import com.zhuzhu.mybatis.pojo.Employee;
import com.zhuzhu.mybatis.pojo.EmployeeExample;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;

public class TestMBG {
    private SqlSession session;
    private Logger log = LoggerFactory.getLogger(TestMBG.class);
    // junit会在每一个@Test方法前执行@Before方法
    @Before
    public void init() throws IOException {
        session = new SqlSessionFactoryBuilder()
                .build(
                        Resources.getResourceAsStream("mybatis-config.xml"))
                .openSession();
        //当openSession中的值是true的时候,就会自动提交事务
    }

    // junit会在每一个@Test方法后执行@After方法
    @After
    public void clear() {
        //由于使用的是JDBC,所以不会自动提交事务,所以这里手动提交
        session.commit();
        session.close();
    }
    @Test
    public  void testMBG(){
        EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
        //查询所有数据
        PageHelper.startPage(1,3);
        List<Employee> list = employeeMapper.selectByExample(null);
        list.forEach(employee -> System.out.println(employee));
        PageInfo<Employee> pageInfo = new PageInfo<>(list);
        System.out.println(pageInfo);
        //获取总条数
        System.out.println("总条数:"+pageInfo.getTotal()); //获取总页数
        System.out.println("总页数"+pageInfo.getPages()); //获取当前页
        System.out.println("当前页"+pageInfo.getPageNum()); //获取每页显示的条数
        System.out.println("每页条数"+pageInfo.getSize());
       
    }
}

at last

This blog’s introduction to the use of Mybatis ends here. If this blog is helpful to you, please click a like to support it. Thank you! See you in the next blog!

Guess you like

Origin blog.csdn.net/weixin_64972949/article/details/131032979