Understanding Java persistence framework under Mybatis (connection pooling, caching, and annotation development)

Keywords: Mybatis SQL xml Java

Preface

It's recommended that you read this article first (above Mybatis, the Java persistence framework for first time) Click to visit

9. Mybatis Connection Pool and Transaction Depth

1. Connection Pool Technology for Mybatis

  • Connection pooling technology in MBatis uses its own connection pooling technology, unlike JDBC's connection pooling technology, which is developed using other vendors such as c3p0 and druid.
  • In the SqlMapConfig.xml configuration file of Mybatis, configure the connection pool in Mybatis by <dataSource type="POOLED">

(1) Classification of Mybatis connection pools

  • In Mybatis, its data source dataSource is divided into the following categories:

  • You can see that Mybatis classifies data sources into three categories:

      UNPOOLED does not use connection pool data sources
      POOLED uses a connection pool's data source
      Data source implemented by JNDI using JNDI
    
  • The structure is as follows:

  • Accordingly, MyBatis internally defines UnpooledDataSource, which implements the java.sql.DataSource interface, and the PooledDataSource class, which represents data sources of UNPOOLED and POOLED types.

  • Among these three data sources, we usually use POOLED data sources (many times we refer to data sources for better data management)
    Library connections, also known as connection pooling technology).

(2) Configuration of data sources in Mybatis

The data source is configured in the SqlMapConfig.xml file as follows:

<!-- Configure data source (connection pool) information -->
<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>

When MyBatis initializes, it creates a corresponding type of data source DataSource based on the type property of <dataSource>that is:

  • type="POOLED": MyBatis creates a PooledDataSource instance
  • type="UNPOOLED": MyBatis creates an UnpooledDataSource instance
  • type="JNDI": MyBatis will look up the DataSource instance from the JNDI service and return to using

All of these are important points. Mybatis's connection pool only needs to be configured in the SqlMapConfig.xml configuration file. Here's an analysis of it

(3) Access to DataSource in Mybatis

MyBatis creates the DataSource object of the data source through factory mode, and MyBatis defines an abstract factory interface: org.apache.ibatis.datasource.DataSourceFactory, which returns the data source DataSource through its getDataSource() method.

  • The following is the DataSourceFactory source code, which is as follows:
package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
/**
* @author Clinton Begin
*/
public interface DataSourceFactory {
    void setProperties(Properties props);
    DataSource getDataSource();
}

When MyBatis creates a DataSource instance, it is placed in the Environment object inside the Configuration object for later use.

The detailed analysis process is as follows:

  • Advance into the XMLConfigBuilder class to find the following code:
  • Analyzing the environment property of the configuration object yields the following results:

(4) Analysis of the acquisition process of connections in Mybatis



When we need to create a SqlSession object and execute an SQL statement, MyBatis calls the dataSource object to create a java.sql.Connection object.That is, the creation of the java.sql.Connection object is delayed until the SQL statement is executed.

@Test
public void testSql() throws Exception {
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = factory.openSession();
    List<User> list = sqlSession.selectList("findUserById",41);
    System.out.println(list.size());
}

Only when the fourth sentence of sqlSession.selectList("findUserById") triggers MyBatis to execute the following at the bottom level
Method to create a java.sql.Connection object.

Here is the source code for the connection:

  • Finally, we can see that the point at which the real connection opens is only when the SQL statement is executed.In fact, by doing so, you can further discover that database connections are the most valuable resource. You only get and open connections when you want to use them, and then return the database connections to the connection pool as soon as they are exhausted.

2. Transaction Control for Mybatis

(1) Review of transactions in JDBC

In JDBC, we can manually change the commit of a transaction to manual, which can be adjusted by the setAutoCommit() method.

  • Through the JDK documentation, we found the following methods:

Because the Mybatis framework encapsulates JDBC, the transaction control mode of the Mybatis framework itself uses the JDBC setAutoCommit() method to set the transaction commit mode.

(2) Transaction commit method in Mybatis

The way transactions are committed in Mybatis is essentially by calling setAutoCommit() of JDBC to implement transaction control.

  • Run the following code:
public class MybatisTest {
    private InputStream in;
    private SqlSession sqlSession;
    private UserDao userDao;

    @Before
    public void init() throws IOException{
        //1. Read profile to generate byte input stream
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. Get SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3. Get SqlSession object
        sqlSession = factory.openSession();
        //4. Get the proxy object for dao
        userDao = sqlSession.getMapper(UserDao.class);

    }

    @After
    public void destory() throws IOException {
        //Submit Transaction
        sqlSession.commit();
        //6. Release Resources
        sqlSession.close();
        in.close();
    }
    /**
     * Test Save Operation
     */
    @Test
    public void testSave() throws IOException {
        User user = new User();
        user.setUsername("autocommit");
        user.setAddress("Xi'an City, Shaanxi Province");
        user.setSex("male");
        user.setBirthday(new Date());
        System.out.println("Before saving:"+user);
        //5. Execute save method
        userDao.saveUser(user);
        System.out.println("After saving:"+user);
    }
}
  • Run result:

This is the whole process of Connection change. Through analysis we can find that during previous CUD operations we had to commit transactions manually because the setAutoCommit() method, whose value was set to false at execution time (default is false), had to commit through sqlSession.commit(true) in CUD operations.

(3) Settings for Mybatis to automatically commit transactions

Once sqlSession.commit(true) is set, it can be submitted automatically.

public class MybatisTest {
    private InputStream in;
    private SqlSession sqlSession;
    private UserDao userDao;

    @Before
    public void init() throws IOException{
        //1. Read profile to generate byte input stream
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. Get SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3. Get SqlSession object
        sqlSession = factory.openSession(true);
        //4. Get the proxy object for dao
        userDao = sqlSession.getMapper(UserDao.class);
    }

    @After
    public void destory() throws IOException {
        //6. Release Resources
        sqlSession.close();
        in.close();
    }
    /**
     * Test Save Operation
     */
    @Test
    public void testSave() throws IOException {
        User user = new User();
        user.setUsername("autocommit");
        user.setAddress("Xi'an City, Shaanxi Province");
        user.setSex("male");
        user.setBirthday(new Date());
        System.out.println("Before saving:"+user);
        //5. Execute save method
        userDao.saveUser(user);
        System.out.println("After saving:"+user);
    }
}

At this point the transaction is set to autocommit, which also enables the preservation of records during CUD operations.Although this is also a way, it is more common programmatically to set the autocommit mode to false and then decide whether to commit based on the situation.Because we can decide whether to submit or not based on business conditions.

10. Dynamic SQL statements for Mybatis

  • Referenced official documents are described as follows:

1. Label <if>for dynamic SQL

We use different SQL statements to query based on different values of the entity class.For example, if the ID is not empty, you can query based on the id, and if the username is not empty at the same time, you can also add the user name as a condition.This situation is often encountered in our multi-conditional combination queries.

(1) Persistence Layer Dao Interface

/**
 * Query all users
 * @return
 */
List<User> findAll();

(2) Persistence Layer Dao Mapping Configuration

<select id="findByUser" resultType="user" parameterType="user">
    select * from user where 1=1
    <if test="username!=null and username != '' ">
        and username like #{username}
    </if>
    <if test="address != null">
        and address like #{address}
    </if>
</select>

Note: The attribute name of the object is written in the test attribute of the tag, and the OGNL expression is used for wrapper class objects.Also note the role of where 1=1~!

2. The <where>label of dynamic SQL

To simplify the conditional assembly of where 1=1 above, we can use the <where>tag to simplify development.

Persistence Layer Dao Mapping Configuration

<!--Extract duplicates sql Sentence-->
<sql id="defaultUser">
    select * from user
</sql>
<!-- Query based on user information -->
<select id="findByUser" resultType="user" parameterType="user">
    <include refid="defaultSql"></include>
    <where>
        <if test="username!=null and username != '' ">
            and username like #{username}
        </if>
        <if test="address != null">
            and address like #{address}
        </if>
    </where>
</select>

3. The <foreach>label of dynamic labels

(1) Requirements

Input multiple IDs to query user information, using the following two sql s:

SELECT * FROM USERS WHERE username LIKE '%Zhang%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%Zhang%' AND id IN (10,89,16)

So when we do range queries, we dynamically add values from a set as parameters.

How do I pass parameters?

  • Add a List collection to QueryVo to encapsulate parameters
/**
* Query Conditions
*/
public class QueryVo implements Serializable {
    private List<Integer> ids;
    public List<Integer> getIds() {
        return ids;
    }
    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}

(2) Persistence Layer Dao Interface

/**
* Query users based on id collection
* @param vo
* @return
*/
List<User> findInIds(QueryVo vo);

(3) Persistence Layer Dao Mapping Configuration

<!-- Query all users in id In a collection of -->
<select id="findInIds" resultType="user" parameterType="queryvo">
    <!-- select * from user where id in (1,2,3,4,5); -->
    <include refid="defaultSql"></include>
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="id in ( " close=")" item="uid"
            separator=",">
                #{uid}
            </foreach>
        </if>
    </where>
</select>
  • SQL statement: select field from user where id in (?)
  • The <foreach>tag is used to traverse a collection and its properties:
    • Collection: represents the collection elements to traverse, be careful not to write #{}
    • open: Represents the beginning of a statement
    • close:Represents the end
    • item: Represents each element of a traversed collection, resulting variable name
    • sperator: for separator

4. Simplified SQL fragments in Mybatis

Sql can extract duplicate SQL and use include reference to achieve SQL reuse.(already used in the above example)

  • Define snippets
<!-- Extracting duplicate statement code snippets -->
<sql id="defaultSql">
    select * from user
</sql>
  • Reference snippet
<!-- Configure Query All Operations -->
<select id="findAll" resultType="user">
    <include refid="defaultSql"></include>
</select>
<!-- according to id query -->
<select id="findById" resultType="UsEr" parameterType="int">
    <include refid="defaultSql"></include>
    where id = #{uid}
</select>

11. One-to-many for Mybatis multi-table queries

This case focuses on the simplest User and account model to analyze Mybatis multitable relationships.The User is the Use r table and the account is the Account table.A User can have multiple accounts.Specific relationships are as follows:

1. One-to-one query (many-to-one)

demand

Query all account information, and associate query for single user information.

  • Note: Since an account information can only be used by a user, the associated query of user information from querying account information is a one-to-one query.Querying account information under a user from user information is a one-to-many query because a user can have multiple accounts.

Mode 1

  • Entity class that defines account information
/**
* Description: Entity class of account
*/
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public Double getMoney() {
        return money;
    }
    public void setMoney(Double money) {
        this.money = money;
    }
    @Override
    public String toString() {
        return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
    }
}
  • Write Sql statements
    • In order to query account information, the corresponding user information of the account should also be queried.
SELECT account.*,user.username,user.address FROM account, user WHERE account.uid = user.id
  • Define AccountUser Class
    • In order to encapsulate the query results of the above SQL statement, the AccountCustomer class is defined to contain both account information and user information, so we can inherit the User class when defining the AccountUser class.
public class AccountUser extends Account implements Serializable {
    private String username;
    private String address;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return super.toString() + " AccountUser [username=" + username + ",address=" + address + "]";
    }
}
  • Define the persistence layer Dao interface for the account
/**
*
* Account Persistence Interface
*/
public interface AccountDao {
    /**
     * Query all accounts and get the account's user name and address information
     * @return
     */
    List<AccountUser> findAll();
}
  • Define query configuration information in the AccountDao.xml 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="com.allen.dao.AccountDao">
    <!-- Configure Query All Operations-->
    <select id="findAll" resultType="accountuser">
        select a.*,u.username,u.address from account a,user u where a.uid =u.id;
    </select>
</mapper>

Note: Because the results of the above query contain account information as well as user information, the value of our return value type returnType is set to AccountUser type, which allows us to receive account information and user information.

  • Summary:
    Defines a special po class as the output type, which defines all fields of the sql query result set.This method is simple and widely used in Enterprises

Mode 2

Using resultMap, define a dedicated resultMap to map one-to-one query results.

  • From the object-oriented (has a) relationship, we know that we can add an object of the User class to the Account class to represent who the account is.

  • Modify Account Class

    • An object that adds a User class to the Account class is a property of the Account class.
/**
*
* Entity class of account
*/
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    private User user;
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public Double getMoney() {
        return money;
    }
    public void setMoney(Double money) {
        this.money = money;
    }
    @Override
    public String toString() {
        return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
    }
}

  • Modify methods in AccountDao interface
/**
* Account Persistence Interface
*/
public interface AccountDao {
    /**
    * Query all accounts and get the account's user name and address information
    * @return
    */
    List<Account> findAll();
}
  • Note: The second way is to change the return value to Account type.Because the Account class contains an object of the User class, it encapsulates the user information for the account.

  • Redefine AccountDao.xml 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="com.allen.dao.AccountDao">
    <!-- Establish correspondence -->
    <resultMap type="account" id="accountMap">
        <id column="aid" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
        <!-- It is used to specify a reference entity property from a table -->
        <association property="user" javaType="user">
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="birthday" property="birthday"/>
            <result column="address" property="address"/>
        </association>
    </resultMap>
    <select id="findAll" resultMap="accountMap">
        select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;
    </select>
</mapper>

2. One-to-many queries

demand

Query all user information and user-associated account information.

Analysis

The user information and his account information are one-to-many relationships, and if the user does not have account information during the query process, we also want to query the user information at this time. We think the left outer join query is more appropriate.

(1) Writing SQL statements

SELECT u.*, acc.id id,acc.uid,acc.money FROM user u
LEFT JOIN account acc ON u.id = acc.uid;

(2) User class joins List<Account>

/**
* Description: User's Entity Class
*/
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Account> accounts;
    public List<Account> getAccounts() {
        return accounts;
    }
    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ",sex=" + sex + ", address=" + address + "]";
    }
}

(3) Add query method to user persistence layer Dao interface

/**
* Query all users and get all account information under each user
* @return
*/
List<User> findAll();

(4) User persistence layer Dao mapping file configuration

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.allen.dao.UserDao">
    <resultMap type="user" id="userMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- collection Is the corresponding relationship used to establish set attributes in one-to-many
        ofType Data type used to specify collection elements
        -->
        <collection property="accounts" ofType="account">
            <id column="aid" property="id"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
        </collection>
    </resultMap>
    <!-- Configure Query All Operations -->
    <select id="findAll" resultMap="userMap">
        select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid
    </select>
</mapper>
  • The collection section defines the account information associated with the user.Represents the result set of an associated query
  • property="accList": Which property of the result set of the associated query is stored on the User object.
  • ofType="account": Specifies the object type in the result set of the associated query, that is, the object type in the List.You can use an alias or a fully qualified name here.

12. Many-to-many of Mybatis multi-table queries

1. Implement Role to User many-to-many

A many-to-many relationship can actually be thought of as a two-way one-to-many relationship.

(1) Relationship model between users and roles

The many-to-many relationship model between users and roles is as follows:

Add role tables, intermediate tables of user roles, to the MySQL database.

  • Role Table
  • User Role Intermediate Table

(2) Business requirements and implementation of SQL

  • Requirements: Implements a query of all objects and loads the user information it assigns.
  • Analysis:
    • To query roles, we need to use the Role table, but the information of the users assigned to the roles is not directly found, but is related to the user information through the intermediate table (USER_ROLE table).
  • The following is the SQL statement implemented:
SELECT r.*,u.id uid,u.username username,u.birthday birthday,u.sex sex,u.address address FROM ROLE r
INNER JOIN USER_ROLE ur ON ( r.id = ur.rid)
INNER JOIN USER u ON (ur.uid = u.id);

(3) Write role entity classes

public class Role implements Serializable {
    private Integer roleId;
    private String roleName;
    private String roleDesc;
    //Many-to-many relationship mapping: a role can be assigned to multiple users
    private List<User> users;
    public List<User> getUsers() {
        return users;
    }
    public void setUsers(List<User> users) {
        this.users = users;
    }
    public Integer getRoleId() {
        return roleId;
    }
    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    public String getRoleDesc() {
        return roleDesc;
    }
    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }
    @Override
    public String toString() {
        return "Role{" + "roleId=" + roleId + ", roleName='" + roleName + '\'' + ", roleDesc='" + roleDesc + '\'' + '}';
    }
}

(4) Write Role persistence layer interface

public interface RoleDao {
 /**
 * Query all roles
 * @return
 */
 List<Role> findAll();
}

(5) Writing mapping files

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.allen.dao.RoleDao">
     <!--Definition role Table ResultMap-->
     <resultMap id="roleMap" type="role">
         <id property="roleId" column="rid"></id>
         <result property="roleName" column="role_name"></result>
         <result property="roleDesc" column="role_desc"></result>
         <collection property="users" ofType="user">
             <id column="id" property="id"></id>
             <result column="username" property="username"></result>
             <result column="address" property="address"></result>
             <result column="sex" property="sex"></result>
             <result column="birthday" property="birthday"></result>
         </collection>
     </resultMap>
     <!--Query all-->
     <select id="findAll" resultMap="roleMap">
        select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid 
     </select>
</mapper>

2. Implement Many-to-Many User-to-Role

Starting with User, we can also see that a user can have multiple roles, so the user-to-role relationship is still one-to-many.This allows us to think that the many-to-many relationship between User and Role can be broken down into two one-to-many relationships.The implementation process is similar to Role to User's many-to-many implementation process, which is not covered here

13. Mybatis Delay Loading Policy

Many times during the actual development process, we don't always have to load user information to load his account information.This is what we call delayed loading.

1. What is delay loading?

  • Delayed loading: Loading occurs when data is needed and not when data is not needed.Delayed loading is also known as lazy loading.
  • Benefits: Querying from a single table before associating queries from related tables when needed greatly improves database performance, since querying from a single table is faster than querying multiple tables from an association.
  • The disadvantage is that database queries are only performed when data is needed, which can result in longer user wait times and a reduced user experience when querying large amounts of data.

2. Realizing Needs

  • Requirements: Query account information and associate query user information.If you query the Account information first, you can do so. When you need to query the User information, you can query the User information again.Delayed loading is when the user information is queried on demand.
  • When mybatis implements multitable operations, resultMap is used to implement one-to-one, one-to-many, many-to-many relationships.One-to-one and one-to-many mappings are implemented mainly through association and collection.Association, collection have deferred load function.

3. Delay Loading Using Association

demand

Query account information and user information.

(1) Durable Layer DAO Interface of Account

/**
* Account Persistence Interface
*/
public interface AccountDao {
    /**
    * Query all accounts and get the account's user name and address information
    * @return
    */
    List<Account> findAll();
}

(2) Persistent Layer Mapping File for Accounts

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.allen.dao.AccountDao">
    <!-- Establish correspondence -->
    <resultMap type="account" id="accountMap">
        <id column="aid" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
        <!-- It is used to specify a reference entity property from a table -->
        <association property="user" javaType="user" select="com.allen.dao.UserDao.findById" column="uid">
        </association>
    </resultMap>
    <select id="findAll" resultMap="accountMap">
        select * from account
    </select>
</mapper>
  • Select: Fill in the id of the select map we want to call
  • column: Fill in the parameters we want to pass to the select Map

(3) User's persistence layer interfaces and mapping files

  • Persistent Layer Interface
/**
*
* User's Business-tier Interface
*/
public interface UserDao {
/**
* Query by id
* @param userId
* @return
*/
User findById(Integer userId);
}
  • 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">
<mapper namespace="com.allen.dao.UserDao">
<!-- according to id query -->
<select id="findById" resultType="user" parameterType="int" >
select * from user where id = #{uid}
</select>
</mapper>

(4) Turn on Mybatis's delay loading policy

The official documentation for Mybaits, settings, has the following instructions:

Add a delayed loading configuration to the Mybatis configuration file SqlMapConfig.xml

<!-- Turn on support for delayed loading -->
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

(5) Write tests to check only account information but not user information

/**
* One-to-many account operation
*/
public class AccountTest {
    private InputStream in ;
    private SqlSessionFactory factory;
    private SqlSession session;
    private AccountDao accountDao;
    @Test
    public void testFindAll() {
        //6. Perform operations
        List<Account> accounts = accountDao.findAll();
    }
    @Before//Execute before test method execution
    public void init()throws Exception {
        //1. Read the configuration file
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. Create Builder Object
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3. Create SqlSession Factory Object
        factory = builder.build(in);
        //4. Create SqlSession object
        session = factory.openSession();
        //5. Create Dao's proxy object
        accountDao = session.getMapper(AccountDao.class);
    }
    @After//Execute after test method execution is complete
    public void destroy() throws Exception{
        //7. Release Resources
        session.close();
        in.close();
    }
}

The test results are as follows:

  • Since this time only the Account object is queried out and put into the List collection, and no User object is involved, no SQL statement is issued to query the User object associated with the account.

4. Delay Loading Using Collection

The <collection>node also has a select attribute, a column attribute.

demand

When the user object is finished loading, query the account information owned by the user.

(1) Add List<Account>attribute to User entity class

/**
* User's Entity Class
*/
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Account> accounts;
    public List<Account> getAccounts() {
        return accounts;
    }
    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", birthday=" + birthday  + ", sex=" + sex + ", address=" + address + "]";
    }
}

(2) Ways to write user and account persistence layer interfaces

/**
* Query all users and get all account information under each user
* @return
*/
List<User> findAll();
/**
* Query account information based on user id
* @param uid
* @return
*/
List<Account> findByUid(Integer uid);

(3) Write user persistence layer mapping configuration

<resultMap type="user" id="userMap">
    <id column="id" property="id"></id>
    <result column="username" property="username"/>
    <result column="address" property="address"/>
    <result column="sex" property="sex"/>
    <result column="birthday" property="birthday"/>
    <!-- collection Is the corresponding relationship used to establish set attributes in one-to-many
    ofType Data type used to specify collection elements
    select Is the unique identification used to specify the query account (for the account) dao Fully qualified class name plus method name)
    column Is used to specify which field's value to use as a conditional query
    -->
    <collection property="accounts" ofType="account" select="com.allen.dao.AccountDao.findByUid" column="id"></collection>
</resultMap>
<!-- Configure Query All Operations -->
<select id="findAll" resultMap="userMap">
    select * from user
</select>
  • <collection>label: used primarily to load associated collection objects
  • select property: the sql statement used to specify the query account list, so the id of the sql mapping is filled in
  • Column attribute: the parameter source of the sql statement used to specify the select attribute. The above parameter comes from the id column of the user, so it is written as the field name id

(4) Write Account Persistence Layer Mapping Configuration

<!-- According to user id Query account information -->
<select id="findByUid" resultType="account" parameterType="int">
    select * from account where uid = #{uid}
</select>

(5) Test only loads user information

/**
*
* One-to-many operation
*/
public class UserTest {
    private InputStream in ;
    private SqlSessionFactory factory;
    private SqlSession session;
    private UserDao userDao;
    @Test
    public void testFindAll() {
        //6. Perform operations
        List<User> users = userDao.findAll();
    }
    @Before//Execute before test method execution
    public void init()throws Exception {
        //1. Read the configuration file
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. Create Builder Object
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3. Create SqlSession Factory Object
        factory = builder.build(in);
        //4. Create SqlSession object
        session = factory.openSession();
        //5. Create Dao's proxy object
        userDao = session.getMapper(UserDao.class);
    }
    @After//Execute after test method execution is complete
    public void destroy() throws Exception{
         
    }
}

The test results are as follows:

  • Account account information was not loaded.

14. Mybatis Cache

Like most persistence frameworks**, Mybatis also provides caching strategies to improve performance by reducing the number of queries to the database.**

  • Caches in Mybatis are divided into one-level caches and two-level caches.

1. Mybatis Level 1 Cache

(1) Prove the existence of the first level cache

A first-level cache is a SqlSession-level cache that exists as long as the SqlSession has no flush or close.

  • Write user persistence layer Dao interface
/**
*
* User's Business Layer Interface </p>
*/
public interface UserDao {
/**
* Query by id
* @param userId
* @return
*/
User findById(Integer userId);
}
  • Write user persistence layer 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">
<mapper namespace="com.allen.dao.UserDao">
    <!-- according to id query -->
    <select id="findById" resultType="UsEr" parameterType="int" useCache="true">
        select * from user where id = #{uid}
    </select>
</mapper>
  • Write test methods
@Test
public void testFindById() {
    //6. Perform operations
    User user = userDao.findById(41);
    System.out.println("Users for the first query:"+user);
    User user2 = userDao.findById(41);
    System.out.println("Second query user:"+user2);
    System.out.println(user == user2);
}
  • The test results are as follows:
    • You can see that although you queried the code above twice, you only performed one database operation at the end, which is where the first-level cache provided by Mybatis works.Because of the existence of the first-level cache, the second query for records with id 41 does not issue sql statements to query data from the database, but from the first-level cache.

(2) First-level Cache Analysis

A first-level cache is a SqlSession-wide cache, which is emptied when SqlSession modifications, adds, deletes, commit(), close() and other methods are called.

  • Initiate the first query for user information with user id 1, first find if there is user information with id 1 in the cache, and if not, query user information from the database.
  • Get the user information and store it in the first level cache.
  • If sqlSession does a commit (insert, update, delete), empty the first level cache in SqlSession to keep the latest information stored in the cache and avoid dirty reads.
  • The second time I launch a query for user information with user id 1, I first go to find if there is user information with user id 1 in the cache, there is, directly from the cache
    Get user information in.

(3) Testing first-level cache emptying

/**
 * Test Level 1 Cache
 */
@Test
public void testFirstLevelCache(){
    User user1 = userDao.findById(41);
    System.out.println(user1);

//        sqlSession.close();
//        //Get sqlsession object again
//        sqlSession = factory.openSession();
//
    sqlSession.clearCache();//This method can also empty the cache
    userDao = sqlSession.getMapper(UserDao.class);
    User user2 = userDao.findById(41);
    System.out.println(user2);
    System.out.println(user1 == user2);
}

/**
 * Test Cache Synchronization
 */
@Test
public void testClearCache(){
    //1. Query users based on id
    User user1 = userDao.findById(41);
    System.out.println(user1);
    //2. Update user information
    user1.setUsername("update user clear cache");
    user1.setAddress("Zhou Zhi County, Xi'an City");
    userDao.updateUser(user1);
    //3. Query the user with id 41 again
    User user2 = userDao.findById(41);
    System.out.println(user2);
    System.out.println(user1 == user2);
}
  • When sqlSession.close() is executed, the sqlSession is retrieved again and the User object with id=41 is queried, then the sql statement is executed again and the query operation is performed from the database.

2. Mybatis Level 2 Cache

Secondary caches are mapper mapping level caches. Multiple SqlSessions operate on the same Mapper mapping sql statement. Multiple SqlSessions can share the second level caches. Secondary caches are across SqlSessions.

(1) Secondary cache structure diagram

  • First turn on the secondary cache for mybatis.
  • sqlSession1 queries user information, which stores the query data in a secondary cache.
  • If SqlSession3 executes sql under the same mapper mapping and commit submission, the data of the secondary cache area under the mapper mapping will be cleared.
  • When sqlSession2 queries the same user information as sqlSession1, it first goes to the cache to find out if there is data, and if there is data, it takes it out of the cache directly.

(2) Open and close of secondary cache

  • Step 1: Open the secondary cache in the SqlMapConfig.xml file
<settings>
    <!-- Turn on support for secondary caching -->
    <setting name="cacheEnabled" value="true"/>
</settings>

This step can be omitted because cacheEnabled defaults to true.Turn on the second-level cache for true; turn on the second-level cache for false.

  • Step 2: Configure the associated Mapper mapping file

The <cache>tag indicates that the current mapper map will use a secondary cache, depending on the namespace value of the mapper.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.allen.dao.UserDao">
    <!-- Turn on support for secondary caching -->
    <cache></cache>
</mapper>
  • Step 3: Configure the useCache property on the statement
<!-- according to id query -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
    select * from user where id = #{uid}
</select>

Setting useCache="true" in the <select>tag in the UserDao.xml mapping file indicates that the current statement is using a secondary cache, which can be set to false if not.

  • Note: For each query that requires the latest data sql, set useCache=false to disable secondary caching.

(3) Level 2 cache testing

@Test
public void testFirstLevelCache(){
    SqlSession sqlSession1 = factory.openSession();
    UserDao dao1 = sqlSession1.getMapper(UserDao.class);
    User user1 = dao1.findById(41);
    System.out.println(user1);
    sqlSession1.close();//Level 1 cache disappeared

    SqlSession sqlSession2 = factory.openSession();
    UserDao dao2 = sqlSession2.getMapper(UserDao.class);
    User user2 = dao2.findById(41);
    System.out.println(user2);
    sqlSession2.close();
    System.out.println(user1 == user2);
}

After the above test, we found that we executed two queries, and after the first query, we closed the first level cache, then executed the second query, we found that no sql statement was issued to the database, so the data can only come from what we call the second level cache.

(4) Notes on secondary cache

When using a secondary cache, the cached class must implement the java.io.Serializable interface, which allows you to save objects in a serialized manner.

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

15. Mybatis Annotation Development

Annotation development has become more and more popular over the years, and Mybatis can also use annotation development to reduce the need to write Mapper mapping files.

1. Notes on mybatis

@Insert:Add New
 @Update:Implement Updates
 @Delete:Implement Delete
 @Select:Implement Query
 @Result:Implement result set encapsulation
 @Results:Can be used with @Result to encapsulate multiple result sets
 @ResultMap: Encapsulate references to @Results definitions
 @One:Implement one-to-one result set encapsulation
 @Many:Implement one-to-many result set encapsulation
 @SelectProvider: Implement dynamic SQL mapping
 @CacheNamespace: Implement the use of annotation secondary caches

2. Implement basic CRUD using Mybatis annotations

CRUD operations on single tables are the most basic operations, so they must be mastered.

(1) Write entity classes

/**
*
*  User's Entity Class
*/
public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public Date getUserBirthday() {
        return userBirthday;
    }
    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }
    public String getUserSex() {
        return userSex;
    }
    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }
    public String getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }
    @Override
    public String toString() {
        return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex=" + userSex + ", userAddress=" + userAddress + "]";
    }
}

Note: Here we intentionally do not match the column names of the database tables.

(2) Develop persistent layer interfaces using annotations

/**
* User's Persistence Layer Interface
*/
public interface UserDao {
    /**
    * Query all users
    * @return
    */
    @Select("select * from user")
    //Encapsulation of data because the attribute and column names in the User entity class are different, and if they are the same, Result is not used to encapsulate the results
    @Results(id="userMap",
    value= {
        @Result(id=true,column="id",property="userId"),
        @Result(column="username",property="userName"),
        @Result(column="sex",property="userSex"),
        @Result(column="address",property="userAddress"),
        @Result(column="birthday",property="userBirthday")
    })
    List<User> findAll();
    /**
    * Query a user based on id
    * @param userId
    * @return
    */
    @Select("select * from user where id = #{uid} ")
    @ResultMap("userMap")
    User findById(Integer userId);
    /**
    * Save Operation
    * @param user
    * @return
    */
    @Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address} )")
    @SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before = false, statement = { "select last_insert_id()" })
    int saveUser(User user);
    /**
    * update operation
    * @param user
    * @return
    */
    @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id =#{id} ")
    int updateUser(User user);
    /**
    * delete user
    * @param userId
    * @return
    */
    @Delete("delete from user where id = #{uid} ")
    int deleteUser(Integer userId);
    /**
    * Query uses aggregate functions
    * @return
    */
    @Select("select count(*) from user ")
    int findTotal();
    /**
    * Fuzzy Query
    * @param name
    * @return
    */
    @Select("select * from user where username like #{username} ")
    List<User> findByName(String name);
}

By annotating, we no longer need to write a UserDao.xml mapping file.

(3) Write SqlMapConfig configuration file

<?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>
    <!-- To configure properties Location of the file -->
    <properties resource="jdbcConfig.properties"></properties>
    <!-- Configure alias registration -->
    <typeAliases>
        <package name="com.allen.domain"/>
    </typeAliases>
    <!-- Configuration environment -->
    <environments default="mysql">
        <!-- To configure mysql Environment -->
        <environment id="mysql">
        <!-- The type of configuration transaction is JDBC -->
        <transactionManager type="JDBC"></transactionManager>
        <!-- Configure Data Source -->
        <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>
    </environments>
    <!-- Configuration Mapping Information -->
    <mappers>
        <!-- To configure dao The location of the interface in two ways
        First: Use mapper Label Configuration class attribute
        Second: Use package Label, specify directly dao Package in which the interface is located
        -->
        <package name="com.allen.dao"/>
    </mappers>
</configuration>

(4) Write test methods

Omitted here, as configured with xml

3. Using annotations to develop complex relationship mappings

Before implementing complex relationship mapping, we can configure <resultMap>in the mapping file, and we need to use the @Results annotation, @Result annotation, @One annotation, @Many annotation when developing with annotations.

(1) Notes on complex relationship mappings

  • @Results comment
    • Instead, label <resultMap>
    • You can use either a single @Result annotation or a @Result collection in this annotation
    • @Results({@Result(), @Result()}) or @Results(@Result())
  • @Resutl comment
    • Instead of <id>tag and <result>tag
    • Property description in @Result:
      • id primary key field
      • Column name of column database
      • Property requires the assembly property name one, the @One annotation (@Result(one=@One()) to be used, and the @Many annotation (@Result(many=@many()) to be used by many.
  • @One comment (one-to-one)
    • Instead of the <assocation>tag, it is the key to a multitable query, which is used in the comment to specify that the subquery returns a single object.
    • @One Annotation Properties Introduction:
      • select specifies sqlmapper for multitable queries
      • fetchType overrides global configuration parameters
      • lazyLoadingEnabled
    • Use format: @Result (column=", property="), one=@One (select="))
  • @Many comment (many-to-one)
    • Instead of labels, they are the key to multitable queries, which are used in annotations to specify the set of objects returned by subqueries.
    • Note: Aggregated elements are used to handle one-to-many relationships.You need to specify the properties of the mapped Java entity class, the javaType of the properties (typically ArrayList), but the annotations may not define them;
    • Use format: @Result (property=","column="), many=@Many (select="))

(2) Using annotations to map one-to-one complex relationships and delay loading

  • Requirements: Load account information and load user information for the account, allowing for delayed loading as appropriate.(Annotated implementation)

  • Add User entity class and Account entity class

/**
* User's Entity Class
*/
package com.allen.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private String userAddress;
    private String userSex;
    private Date userBirthday;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    public String getUserSex() {
        return userSex;
    }

    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    public Date getUserBirthday() {
        return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userAddress='" + userAddress + '\'' +
                ", userSex='" + userSex + '\'' +
                ", userBirthday=" + userBirthday +
                '}';
    }
}

/**
* Entity class of account
*/
package com.allen.domain;

import java.util.List;

public class Account {

    private Integer id;
    private Integer uid;
    private Double money;

    //Many-to-one (called one-to-one in Mybatis) mapping, where an account can only belong to one user
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

  • Add a persistence layer interface to the account and configure it with annotations
package com.allen.dao;

import com.allen.domain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

public interface AccountDao {
    /**
     * Query all accounts and get the user information to which each account belongs
     * @return
     */
    @Results(id="accountMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one=@One(select="com.allen.dao.UserDao.findById",fetchType= FetchType.EAGER))
    })
    @Select("select * from account")
    List<Account> findAll();

    /**
     * Query account information based on user id
     * @param userId
     * @return
     */
    @Select("select * from account where uid=#{userId} ")
    List<Account> findAccountByUid(Integer userId);
}

  • Add the user's persistence layer interface and configure it with annotations
package com.allen.dao;

import com.allen.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
 * There are four notes for CRUD in mybatis
 *  * @Select @Insert @Update @Delete
 */
@CacheNamespace(blocking = true)
public interface UserDao {
    /**
     * Query all users
     * @return
     */
    @Select("select * from user ")
    @Results(id = "userMap",value = {
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts" ,column = "id",many = @Many(select = "com.allen.dao.AccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();

    /**
     * Query users based on id
     * @param userId
     * @return
     */
    @Select("select * from user where id=#{id} ")
    @ResultMap("userMap")
    User findById(Integer userId);

    /**
     * Fuzzy query based on user name
     * @param username
     * @return
     */
    @Select("select * from user where username like #{username} ")
    @ResultMap("userMap")
    List<User> findUserByName(String username);

}
  • Testing one-to-one associations and delayed loading
/**
*
* Account Test Class
*/
public class AccountTest {
@Test
public void testFindAll() {
    List<Account> accounts = accountDao.findAll();
}

(3) Using annotations to map one-to-many complex relationships

  • Requirements: When querying user information, you should also query his account list.Use annotations.

  • Analysis: A user has multiple account information, thus forming a one-to-many relationship between users and accounts.

  • User entity class joins List<Account>

//One-to-many relationship mapping, one user for multiple accounts
private List<Account> accounts;

public List<Account> getAccounts() {
    return accounts;
}
  • Write user's persistence layer interface and configure it with annotations
package com.allen.dao;

import com.allen.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
 * There are four notes for CRUD in mybatis
 *  * @Select @Insert @Update @Delete
 */
@CacheNamespace(blocking = true)
public interface UserDao {
    /**
     * Query all users
     * @return
     */
    @Select("select * from user ")
    @Results(id = "userMap",value = {
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts" ,column = "id",many = @Many(select = "com.allen.dao.AccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();
}
  • Write the persistence layer interface for the account and configure it with annotations
public interface AccountDao {
    @Select("select * from account where uid = #{uid} ")
    List<Account> findByUid(Integer userId);
}
  • Add Test Method
package com.allen.test;

import com.allen.dao.UserDao;
import com.allen.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

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

public class UserTest {

    private InputStream in;
    private SqlSession sqlSession;
    private UserDao userDao;

    @Before
    public  void init() throws IOException {
        //1. Get byte input stream
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. Build SqlSessionFactory from Byte Input Stream
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3. Produce a SqlSession based on SqlSessionFactory
        sqlSession = factory.openSession(true);
        //4. Use SqlSession to get Dao's proxy object
        userDao = sqlSession.getMapper(UserDao.class);

    }

    @After
    public void destory(){
        //6. Release Resources
        if(sqlSession != null){
            sqlSession.close();
        }
        if (in != null){
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Query all users
     */
    @Test
    public void TestFindAll(){
        //5. Ways to execute Dao
        List<User> users = userDao.findAll();
        /*for (User user : users) {
            System.out.println("-----------Information for each user ---------- ";
            System.out.println(user);
            System.out.println(user.getAccounts());
        }*/
    }
}

4. mybatis annotation-based secondary cache

(1) Turn on secondary cache support in SqlMapConfig

<!--Configure Level 2 Cache-->
<settings>
    <!--Support for turning on secondary caching-->
    <setting name="cacheEnabled" value="true"/>
</settings>

(2) Configuring a secondary cache using annotations in a persistent layer interface

@CacheNamespace(blocking=true)//mybatis implements configuration of secondary cache based on annotations
public interface UserDao {
    //Methods of various CRUD operations
}

summary

Four days later, the review and consolidation of Mybatis was completed, as evidenced by these two blogs, which were too tired.I hope you can give us some encouragement, thank you

58 original articles published, 77 praised, 8387 visited
Private letter follow

Posted by Dave3765 on Fri, 13 Mar 2020 18:28:19 -0700