1. What is delayed loading?
Problem: Configuration and implementation of one-to-one, one-to-many, many-to-many relationships in Mybatis is achievable
Object's associated query. Many times in the actual development process we don't always need to load user information before we have to load his order information. This is what we call delayed loading.
Raise a chestnut
In one-to-many, when we have a user, it has 100 orders Do you want to check out the associated orders when querying the user? Do you want to check out the associated users when querying for orders? A: When querying the user, the order placed by the user should be when to use and when to query. When querying an order, the user information that the order belongs to should be queried along with the order.
**Delayed Loading: ** Loading is done when data is needed and not when data is not needed. Delayed loading is also known as lazy loading
Advantage: Query from a single table first, and then associate queries from related tables when needed, which greatly improves database performance because querying a single table is faster than querying multiple tables from an association. Disadvantages: Because database queries only occur when data is needed, this can cause users to wait longer and experience less when querying large amounts of data because queries also take time. In multiple tables One-to-many, many-to-many: Delayed loading is usually used One-on-one(Many-to-one): Usually immediate loading is used Be careful: Delayed loading is based on nested queries Difference: When the current object and its associated objects are loaded: If its associated object is immediately queried for the current object, it is immediately loaded. Delayed loading occurs when an associated object is looked up;
2. Implement local delayed loading
2.1 Local Delayed Loading
Both association s and collection s tags have a fetchType attribute whose value can be modified to modify local loading policies.
<!--One-to-many nested query: Query all users and at the same time query the orders they have--> <!-- Delayed loading is based on nested queries --> <resultMap id="userMap2" type="user"> <id property="id" column="id"/> <result property="username" column="username"/> <result property="birthday" column="birthday"/> <result property="sex" column="sex"/> <result property="address" column="address"/> <!-- fetchType="lazy" Represents a delayed load policy fetchType="eager" Represents an immediate load policy --> <collection property="ordersList" ofType="orders" fetchType="lazy" select="cn.xuguowen.mapper.OrdersMapper.findById" column="id"/> </resultMap> <select id="findAllWithOrders2" resultMap="userMap2"> select * from user </select>
2.2 Setting the method to trigger delayed loading
I found that after configuring the delayed loading policy, I found that even if no method of the associated object was called, queries of the associated object would be triggered when you called the equals, clone, hashCode, toString methods of the current object.
We can override these four methods in the core configuration file using the lazyLoadTriggerMethods configuration item.
<!--Set method to trigger delayed load policy--> <settings> <!--When the current object's toString()Method uses a delayed load policy--> <setting name="lazyLoadTriggerMethods" value="toString()"/> </settings>
3. Implement global delayed loading
The setting s tag can be used in the core configuration file of MBatis to modify the global loading policy.
<settings> <!--When the current object's toString()Method uses a delayed load policy--> <setting name="lazyLoadTriggerMethods" value="toString()"/> <!--Global Delayed Loading Policy--> <setting name="lazyLoadingEnabled" value="true"/> </settings>
Note that local loading policies take precedence over global ones
<!-- A one-to-one nested query queries all orders while also querying the user information to which each order belongs--> <!-- Turn off global delay loading policy and use immediate loading policy --> <resultMap id="ordersMap2" type="orders"> <id property="id" column="id"/> <result property="ordertime" column="ordertime"/> <result property="total" column="total"/> <result property="uid" column="uid"/> <!--Write here: Consider the second time sql How query statements are introduced, And the first one will be sql Statement queried uid Pass as a parameter to the next day sql In statement Use select Introduce Article 2 sql Statement: Value is second sql Statement's statementid Use column Label will query above uid Pass as a parameter to Article 2 sql In statement --> <association property="user" javaType="user" fetchType="eager" select="cn.xuguowen.mapper.UserMapper.findById" column="uid"/> </resultMap> <select id="findAllWithUser2" resultMap="ordersMap2"> <!-- Note that the return result type cannot be used resultType Automatically encapsulate query results, because Orders There is also an attribute in the entity class user --> select * from orders </select>2. MyBatis Cache
1. Why use caching?
When users frequently query certain fixed data, they query the data from the database for the first time and save it in the cache. When users query the data again, they do not need to query through the database anymore, but query inside the cache. Reduce the wastage caused by network connections and database queries, which improves our query efficiency.Reduce system performance problems caused by high concurrent access.
One sentence summary: Query frequently for data that does not change frequently, and use caching to improve query efficiency.
Like most persistence frameworks, Mybatis also provides a caching strategy that improves performance by allowing a small number of queries to the database. Caches in Mybatis are divided into first-level caches and second-level caches.
2. Level 1 Cache
Level 1 cache is a SqlSeesion level cache and is turned on by default
So when the parameters and SQL statements are exactly the same, we use the same SqlSession object to invoke a Mapper method, often only execute SQL once, because after the first query with SelSession, MyBatis puts it in the cache, and when we query later, SqlSession takes out the current one if no refresh is declared and the cache has not timed outCached data without sending SQL to the database again.
@Test public void testOneCache() throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = factory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); // First Query User user1 = mapper.findById(1); System.out.println(user1); // Second Query User user2 = mapper.findById(1); System.out.println(user2); }
Note that first-level caches are SqlSession-wide caches. Performing C (increase) U (update) D (delete) operations for SqlSession or calling clearCache(), commit(), close() methods will empty the cache and avoid dirty reads.
Cache is cleared for each query
<select flushCache="true"></select>
3. Secondary Cache
The secondary cache is a namspace level (cross sqlSession) cache and is not enabled by default
The opening of the secondary cache needs to be configured. When implementing the secondary cache, MyBatis requires that the POJO returned be serializable. That is, it requires the Serializable interface to be implemented. The configuration method is simple and requires only mapping the XML file configuration to open the secondary cache.
<settings> <!-- 1.name="cacheEnabled" Represents a secondary cache, and the default value is true, 2.value="true" Indicates opening a secondary cache --> <setting name="cacheEnabled" value="true"/> </settings>Configure UserMapper.xml file
<!--Current mapping profile opens secondary cache--> <cache></cache> <!-- useCache="true" Represents the current UserMapper Using a secondary cache --> <select id="findById" resultType="user" parameterType="int" useCache="true"> select * from user where id = # </select>Modify User Entity
public class User implements Serializable { private Integer id; private String username; private Date birthday; private Character sex; private String address; // Relationships that represent many: A list of orders the current user has private List<Orders> ordersList; // Many Relationships: A list of roles the current user has private List<Role> roleList;test result
/** * Verify Level 2 Cache * @throws IOException */ @Test public void testTwoCache() throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession1 = factory.openSession(); UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); User user1 = mapper1.findById(1); System.out.println(user1); // Only when SqlSession.commit() or SqlSession.close is executed will the contents of the first level cache be flushed to the second level cache sqlSession1.close(); SqlSession sqlSession2 = factory.openSession(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User user2 = mapper2.findById(1); System.out.println(user2); sqlSession2.close(); }
4. Analysis of Secondary Cache
5. Serious problems with secondary caches (dirty reads)
**The secondary cache of mybatis has dirty read problems when querying multiple tables because it is namespace level
**
6. Summary
1. mybatis All caches do not require us to store and retrieve data manually. mybatis Maintained automatically. 2. mybatis When the secondary cache is turned on, the query order is: the secondary cache-->Level 1 Cache-->data base 2. Be careful: mybatis Second-level caches can have dirty reading problems and need to be solved using third-party caching technology.3. MyBatis Annotation Development
1. Common Notes for MyBatis
* @Insert: Achieve additions instead of<insert></insert> * @Delete: Delete instead<delete></delete> * @Update: Implement updates instead<update></update> * @Select: Implement the query instead<select></select> * @Result: Implement result set encapsulation instead<result></result> * @Results: Can be used with@Result Use together to encapsulate multiple result sets instead<resultMap></resultMap> * @One: Implement one-to-one result set encapsulation instead<association></association> * @Many: Implement one-to-many result set encapsulation instead<collection></collection>
2.MyBatis annotation development for deletion and addition
1: UserMapper interface
package cn.xuguowen.mapper; import cn.xuguowen.pojo.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; /** * @author Xu Guowen * @create 2021-10-03 17:40 */ public interface UserMapper { /** * Query all user information * @return */ @Select("select * from user") List<User> findAll(); /** * Save user information * @param user */ @Insert("insert into user (username,birthday,sex,address) values (#, #, #,# )") void save(User user); /** * Modify user information based on user id * @param user */ @Update("update user set username = #,birthday = # where id = #") void update(User user); /** * Delete users based on id * @param id */ @Delete("delete from user where id = #") void delete(Integer id); }
Core Profile
<!--We used a mapping file that was replaced by annotations, so we just need to load the one that used annotations Mapper Interface is sufficient--> <mappers> <!--Scan using annotated Mapper class--> <!-- <mapper></mapper> --> <!--Scan using annotated Mapper The package in which the class resides--> <package name="com.lagou.mapper"></package> </mappers>
3: Testing
package cn.xuguowen.test; import cn.xuguowen.mapper.UserMapper; import cn.xuguowen.pojo.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.Date; import java.util.List; /** * @author Xu Guowen * @create 2021-10-03 17:42 */ public class TestAnno { private SqlSessionFactory factory; private SqlSession sqlSession; @Before public void before() throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); factory = new SqlSessionFactoryBuilder().build(is); sqlSession = factory.openSession(); } @After public void after() { sqlSession.commit(); sqlSession.close(); } @Test public void testFindAll() throws IOException { UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.findAll(); for (User user : users) { System.out.println(user); } } @Test public void testSave() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setUsername("Xu Guowen"); user.setBirthday(new Date()); user.setSex('male'); user.setAddress("Datong, Shanxi"); mapper.save(user); } @Test public void testUpdate() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setUsername("Boss Huang"); user.setBirthday(new Date()); user.setId(6); mapper.update(user); } @Test public void testDelete() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.delete(8); } }
3.MyBatis annotation development for complex mapping development
Previously, we implemented complex relationship mapping in the mapping file by configuring resultMap, association, collection.
After developing with annotations, we can use @Results, @Result, @One, @Many annotations to configure complex relationships
4.MyBatis annotation development for one-to-one query
4.1 Requirement: Query order information and at the same time query the user information to which the order belongs
One-to-one Query StatementSELECT * FROM orders; SELECT * FROM `user` WHERE id = #;
4.2 Code implementation
a) OrderMapper interface and UserMapper interface/** * Query all orders and at the same time query the user information to which the order belongs * @return */ @Select("select * from orders") @Results({ @Result(property = "id",column = "id"), @Result(property = "ordertime",column = "ordertime"), @Result(property = "total",column = "total"), @Result(property = "uid",column = "uid"), @Result(property = "user",javaType = User.class,column = "uid", one = @One(select = "cn.xuguowen.mapper.UserMapper.findById", fetchType = FetchType.EAGER)) }) List<Orders> findAllWithUser();
/** * Annotations implement one-to-one queries: query all orders and the user information to which the order belongs * @param uid * @return */ @Select("select * from user where id = #") User findById(Integer uid);b) Test code
/** * Test one-to-one query: query all orders and the user information to which the order belongs */ @Test public void testOneToOne() { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); List<Orders> allWithUser = mapper.findAllWithUser(); for (Orders orders : allWithUser) { System.out.println(orders); } }
5.MyBatis annotation development for one-to-many queries
5.1 Requirements: Query all users and the order information they have
One-to-many query statementSELECT * FROM user; SELECT * FROM `orders` WHERE uid = #;
5.2 Code implementation
a) UserMapper interface and OrdersMapper interface/** * Annotations implement one-to-many queries: query all users and find out what order information they have * @return */ @Select("select * from user") @Results({ @Result(property = "id",column = "id",id = true), @Result(property = "username",column = "username"), @Result(property = "birthday",column = "birthday"), @Result(property = "sex",column = "sex"), @Result(property = "address",column = "address"), @Result(property = "ordersList",javaType = List.class,column = "id", many = @Many(select = "cn.xuguowen.mapper.OrderMapper.findById") ) }) List<User> findAllWithOrders();
/** * Annotations implement one-to-many queries: query all users and find out what order information they have * @param uid Query out the order information the user has based on the user id * @return */ @Select("select * from orders where uid = #") List<Orders> findById(Integer uid);b) Test code
/** * Test a one-to-many query: query all users and the order information they have */ @Test public void testOneToMany() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.findAllWithOrders(); for (User user : users) { // In the core configuration file: Configured to trigger the delayed load policy when the toString() method of the current object is called System.out.println(user); // Now I need to query the user's order information System.out.println(user.getOrdersList()); } }
6.MyBatis annotation development for many-to-many queries
6.1 Requirement: Query all users and all roles of that user at the same time
Many-to-Many Query StatementSELECT * FROM user; SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON r.id = ur.roleid WHERE ur.userid = #;
6.2 Code implementation
a) UserMapper interface and RoleMapper interface/** * Annotations enable many-to-many queries: query all users and the role information they have * @return */ @Select("select * from user") @Results({ @Result(property = "id",column = "id",id = true), @Result(property = "username",column = "username"), @Result(property = "birthday",column = "birthday"), @Result(property = "sex",column = "sex"), @Result(property = "address",column = "address"), @Result(property = "roleList",javaType = List.class,column = "id", many = @Many(select = "cn.xuguowen.mapper.RoleMapper.findByUserId")) }) List<User> findAllWithRole();
/** * Annotations enable many-to-many queries: query all users and the role information they have * @param uid Query user's role information based on user id * @return */ @Select("SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON r.id = ur.roleid WHERE ur.userid = #") List<Role> findByUserId(Integer uid);b) Test code
/** * Annotations enable many-to-many queries: query all users and the role information they have */ @Test public void testManyToMany() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.findAllWithRole(); for (User user : users) { // Because a global delayed loading policy is set in the core configuration file: the delayed loading mechanism is triggered when the current object toString() method is called System.out.println(user); System.out.println(user.getRoleList()); } }
7.MyBatis annotation development for secondary caching
7.1 Configure Core Profile Support for Opening Secondary Cache
<settings> <!-- 1.name="cacheEnabled" Represents a secondary cache, and the default value is true, 2.value="true" Indicates opening a secondary cache --> <setting name="cacheEnabled" value="true"/> </settings>
7.2 Configuring a secondary cache using annotations in the Mapper interface
/*Use the @CacheNamespace annotation for secondary caching*/ @CacheNamespace public interface UserMapper {...}
8.MyBatis Annotation Development for Delayed Loading Strategy
Whether one-to-one or one-to-many, there is a fetchType attribute in the annotation configuration
* fetchType = FetchType.LAZY Indicates lazy loading * fetchType = FetchType.EAGER Indicates immediate loading * fetchType = FetchType.DEFAULT Indicates use of global configuration,If the value of the global delay loading policy is true,Indicates the use of a delayed load policy; if the value of the global delayed load policy is false,Indicates the use of the immediate load policy. * <!--Global Delayed Loading Policy--> <setting name="lazyLoadingEnabled" value="true"/>
9. Summary
Annotation Development and xml Analysis of Mapping File Configuration 1.For development efficiency: Annotation writing is simpler and more efficient. 2.In terms of maintainability: Notes If you want to modify, you must modify the source code, which will result in increased maintenance costs. xml Mapping profiles are more maintainable. 3.In actual development: two development methods are used in combination. CURD Comments can be used for development; complex queries can be used xml Develop the way configuration files are mapped.