MyBatis sample code
First, MyBatis framework introduction
1. What is a framework?
A Framework is a reusable design of the whole or part of the system. From the perspective of application, a Framework can be regarded as a group of abstract components and the method of interaction between component instances. From the point of view of application purpose, a framework is an application skeleton that can be customized by application developers. In summary, a framework is a half-finished application, a component that helps you develop your own system.
The framework solves the problem
Framework to solve one of the most important problem is the issue of technology integration, in the framework of J2EE, have all kinds of technology, different software companies need to select different from the J2EE technology, which makes the software enterprise applications rely on these technologies, eventually technology its own complexity and risk will impact directly on the application. Application is the core of software enterprises and the key to competitiveness. Therefore, the design of application itself should be decoupled from the specific implementation technology. In this way, the research and development of software enterprises will focus on the design of the application, rather than the specific technical implementation, which is the bottom support of the application and should not directly affect the application.
The framework is generally located in the middle layer between low-level application platform (such as J2EE) and high-level business logic. The framework realizes some functions and can well integrate business logic and functions.
Layering ideas in software development
In order to facilitate the development and management of software, to achieve “high cohesion, low coupling” in software engineering, the problems in development are divided into various solutions, making the application easy to control, easy to expand, easy to manage resources, common hierarchical idea-MVC design thought. The responsibilities of each part are better realized through layers, and then different frameworks are differentiated to solve the problems concerned by each layer.
Common frameworks in layered development
① A framework to solve the problem of data persistence layer –MyBatis
② The framework to solve the web layer problem –springMVC
③ Spring is the framework to solve the problem of technical integration
2. MyBatis framework Introduction
MyBatis is an excellent Java-based persistence layer framework, which encapsulates JDBC internally. Developers only need to pay attention to THE SQL statement itself, and do not need to spend more energy to deal with the complex process of loading drivers, creating connections, creating statements and so on.
MyBatis configures the statement to be executed by annotations or XML configuration, and generates the SQL statement to be executed by mapping Java objects and dynamic parameters of SQL in the statement. Finally, the Mybatis framework executes SQL statements and maps the results to Java objects and returns them.
Mybatis uses ORM to solve the problem of entity class and database mapping, encapsulates JDBC, shields the access details of JDBC underlying API, so that we do not need to deal with JDBC API, but also can complete the database access operation.
Highly recommended reading: MyBatis official Chinese document
MyBatis implements basic CRUD operations
1. Basic configuration process:
① Create Maven project and add coordinate import JAR package
② Write the entity class that maps to the database table
③ Write the persistence layer interface
(4) Write the mapping file of the interface of the persistence layer or write annotations on the interface of the persistence layer
⑤ Write the configuration file
2. Use the persistence layer mapping configuration file
2.1 Mapping Labels in configuration Files
2.1.1 Common Labels
: Used to configure the returned data set and establish the corresponding relationship between the query result column name and the entity class attribute name.
Id attribute: Specifies the name
Type attribute: Specifies the entity class name
- The internal label
<id>
: Used to configure the field corresponding to the keyword in the database table. - The internal label
<result>
: Used to configure fields that correspond to non-key fields in the database.- Property attribute: Used to specify attributes of the entity class
- Colum property: Property used to specify database columns
< SQL > : used to write common SQL statements. You can extract common SQL statements with the
tag.
2.1.2 Query related labels
< INSERT > : Used to write operations related to database inserts
: Used to write operations related to database deletion
: Used to write database update-related operations
< SELECT > : Used to write operations related to database queries
Attributes of tags
- Id attribute: The method name must be the same as that of the persistence layer interface, otherwise the mapped method will not be found.
- ResultType property: Specifies the type of the result set
- ResultMap property: Specifies the result set
- ParameterType property: Specifies the type of the incoming parameter. If a custom class is passed in, write the fully qualified class name of the class
- ParameterMap property: Specifies the incoming parameters
This list of tags is not complete, more information about mapping tags is recommended to refer to the official documentation of MyBatis: MyBatis XML Mapper
2.2 Writing SQL statements in labels
SQL statements are written inside corresponding labels to support complex queries such as combined and aggregated SQL statements.
#{} character in SQL statement: placeholder, equivalent to JDBC? Is used to replace actual data, specific data, when executing SQL statements
It depends on what’s in the braces.
If it is a basic data type, you can write it freely. If it is an object type, you need to use OGNL.
Ognl: Is an expression Language provided by Apache, full name: Object Graphic Navigation Language (Object Navigation Language), it according to a certain syntax format to obtain data, syntax format: #{object. Object}
# {}
与 The ${}
The difference between:
#{} represents a placeholder: #{} allows you to set values to placeholders in prepareStatement, automatically convert Java and JDBC types, and prevent SQL injection. #{} can accept simple type values or POJO attribute values. If parameterType transmits a single simple type value, the #{} parentheses can be value or some other name.
${} represents a concatenation string: ${} concatenates parameterType’s incoming content into SQL without jDBC-type conversions. ${} can accept simple type values or POJO attribute values. If parameterType transmits simple type values, only value can be in parentheses.
Precautions for using mapping files:
- The mapping of the persistence layer interface and the persistence layer interface must be under the same package structure
- The namespace attribute value of the Mapper tag in the persistence layer mapping configuration must be the fully qualified class name of the persistence layer interface
3. Use of MyBatis profile
Configure the information content and sequence
-properties: configures database connection files -- Property: configures single database connection files -- Settings: configures mybatis related --setting -- typeAliases: Plugging -- Environments --typeAliase -- Package -- typeHandlers -- objectFactory -- Plugging -- Environments --environment(environment subproperty object) --transactionManager(transactionManager) --dataSource(dataSource) : references the properties configuration -mappers: Specify mapping file location --mapper(specify mapping class) --resource(relative path resource) --class(mostly used for configuring annotation SQL) : The mapper interface name must be the same as the mapper mapping file name, and in the same path. --package: Mapper interface name and mapper mapping file name must be the same, and in the same path.Copy the code
For more information about the MyBatis configuration file, please refer to the MyBatis XML configuration document
4. Sample code:
Preparations: Execute SQL statements to generate the user table
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'User name',
`birthday` datetime(0) NULL DEFAULT NULL COMMENT 'birthday',
`sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'gender',
`address` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'address'.PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (41.'Lao wang'.'the 2018-02-27 17:47:08'.'male'.'Beijing');
INSERT INTO `user` VALUES (42.'Little Two Kings'.'the 2018-03-02 15:09:37'.'woman'.Chaoyang District, Beijing);
INSERT INTO `user` VALUES (43.'Little Two Kings'.'the 2018-03-04 11:34:34'.'woman'.Chaoyang District, Beijing);
INSERT INTO `user` VALUES (45.'Tom'.'the 2018-03-04 12:04:06'.'male'.Chaoyang District, Beijing);
INSERT INTO `user` VALUES (46.'Lao wang'.'the 2018-03-07 17:37:26'.'male'.'Beijing');
INSERT INTO `user` VALUES (48.'Bruce'.'the 2020-09-14 14:54:47'.'male'.Weihai, Shandong);
INSERT INTO `user` VALUES (56.'Tom'.'the 2020-09-14 14:18:08'.'male'.'Yantai, Shandong');
INSERT INTO `user` VALUES (57.'Jerry'.'the 2020-09-14 14:15:51'.'woman'.'Yantai, Shandong');
INSERT INTO `user` VALUES (60.'Big M'.'the 2020-09-17 11:28:08'.'male'.'Don't understand what?');
INSERT INTO `user` VALUES (67.'Cathy'.'the 2020-09-15 08:41:57'.'male'.Wenzhou, Zhejiang);
INSERT INTO `user` VALUES (68.'update'.'the 2020-09-17 16:13:24'.'male'.'Haidian District, Beijing');
INSERT INTO `user` VALUES (69.'test'.'the 2020-09-17 15:59:56'.'woman'.'Haidian District, Beijing');
INSERT INTO `user` VALUES (70.'test'.'the 2020-09-17 16:07:22'.'woman'.'Haidian District, Beijing');
INSERT INTO `user` VALUES (71.'test'.'the 2020-09-17 16:07:35'.'woman'.'Haidian District, Beijing');
SET FOREIGN_KEY_CHECKS = 1;
Copy the code
① Create the Maven project and add the coordinates
<! -- Configure and compile plugins -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<! Jar package -->
<packaging>jar</packaging>
<! -- Configure third-party dependency libraries -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
Copy the code
② Write the entity class User corresponding to the data
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId(a) {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername(a) {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday(a) {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex(a) {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress(a) {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString(a) {
return "User{" +
"id=" + id +
", username='" + username + '\' ' +
", birthday=" + birthday +
", sex='" + sex + '\' ' +
", address='" + address + '\' ' +
'} '; }}Copy the code
③ Write the persistence layer interface IUserDao
public interface IUserDao {
/** * get all accounts *@return* /
List<User> getUsers(a);
/** * save account *@param user
*/
void saveUser(User user);
/** * Update account *@param user
*/
void updateUserById(User user);
/** * Query user * by id@param userId
* @return* /
User getUserById(Integer userId);
/** * Delete user * by id@param userId
*/
void deleteUserById(Integer userId);
/** * Query user * by name@param name
* @return* /
List<User> getUserByName(String name);
/** * Query the total number of users *@return* /
int getUserTotal(a);
}
Copy the code
(4) Write the mapping file iUserdao.xml for interfaces of the persistence layer
<? 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="cn.bruce.dao.IUserDao"> <! <resultMap id= <resultMap id="userMap" type="user"> <! -- Keyword corresponding to --> <id property="userId" column="id"/ > <! -- non-keyword corresponding --> <result property="userName" column="username"/>
<result property="userSex" column="sex"/>
<result property="userBirthday" column="birthday"/>
<result property="userAddress" column="address"/> </resultMap> <! -- Configure query related statements --> <! --> <select id="getUsers" resultMap="userMap"> select * from user; </select> <! --> <insert id="saveUser" parameterType="user">
<selectKey keyProperty="userId" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id(a);
</selectKey>
insert into user(username, sex, birthday, address) value (#{userName}, #{userSex}, #{userBirthday}, #{userAddress})</insert> <! Update operation --> <update id="updateUserById" parameterType="user"> update user set username = #{userName}, sex = #{userSex}, birthday = #{userBirthday}, address = #{userAddress} where id = #{userId} </update> <! --> <select id="getUserById" parameterType="int" resultMap="userMap"> select * from user where id = #{userId} </select> <! Delete user by id --> <delete id="deleteUserById" parameterType="java.lang.Integer"> delete from user where id = #{userId} </delete> <! --> <select id="getUserByName" parameterType="string" resultMap="userMap"> select * from user where username like #{userName} </select> <! --> <select id="getUserTotal" resultType="int">
select count(id) from user
</select>
</mapper>
Copy the code
⑤ Write the JDBC connection configuration file jdbcconfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/base_crud? characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=* * * * *
Copy the code
⑥ Write the MyBatis configuration file MyBatis -config.xml
<! DOCTYPEconfiguration
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<! The resource property obtains the JDBC connection from the relative path, and the URL property obtains the JDBC connection from the absolute path. You can also specify the JDBC connection by setting the property tag.
<! The first method is to set the relative path of the JDBC connection through the resource property.
<properties resource="jdbcConfig.properties">
<! The second way is to set the absolute path to the JDBC connection through the URL property.
<! --url="file:///E:/workspace/workspace_idea03/demo-mybatis/day02_mybatis01_CRUD/src/main/resources/jdbcConfig.properties" >-->
<! The third way to configure the JDBC connection is to configure the property tag.
<! --<property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/base_crud"/> <property name="username" value="root"/> <property name="password" value="123456"/>-->
</properties>
<! -- typeAliases are used to configure aliases, only class aliases in domain -->
<typeAliases>
<! -- typeAlias is used to configure aliases. The type attribute configures the fully qualified class name of the entity class. The alias attribute specifies the alias, which is case-sensitive -->
<! --<typeAlias type="cn.bruce.domain.User" alias="user"></typeAlias>-->
<! The package used to specify the configuration alias. When specified, the entity class under the package will be registered with the alias, and the class name is the alias, and is case insensitive.
<package name="cn.bruce.domain"/>
</typeAliases>
<! -- Configure the environment, configure the CONTROL of JDBC connections and transactions -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<! -- Configure mapping file -->
<mappers>
<mapper resource="dao/IUserDao.xml"></mapper>
<! The package tag is used to specify the package in which the DAO interface resides. When specified, mapper and resource or class attributes are not needed, but the mapping file and dao interface are required to be in the same package.
<! --<package name="cn.bruce.dao"/>-->
</mappers>
</configuration>
Copy the code
7. Configure log log4j
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=E:test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%t] %-5p %30.30c %x - %m\n
Copy the code
⑧ Write test classes for testing
public class IUserDaoTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
/** * execute * before the test execution method executes@throws IOException
*/
@Before
public void init(a) throws IOException {
// 1. Read the configuration file to generate a byte input stream
in = Resources.getResourceAsStream("mybatis-config.xml");
// 2. Get SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. Obtain the SqlSession object
sqlSession = factory.openSession();
// 4. Obtain the DAO object
userDao = sqlSession.getMapper(IUserDao.class);
}
/** * is used to execute * after the test method is executed@throws IOException
*/
@After
public void destroy(a) throws IOException {
// Commit the transaction
sqlSession.commit();
// Release the connection
sqlSession.close();
in.close();
}
/** * Test query all users */
@Test
public void testGetUsers(a) {
List<User> users = userDao.getUsers();
for(User user : users) { System.out.println(user); }}/** * Test to get user */ by id
@Test
public void testGetUserById(a) {
User user = userDao.getUserById(41);
System.out.println(user);
}
/** * Test update user operation */
@Test
public void testUpdateUser(a) {
User user = new User();
user.setUserId(48);
user.setUserName("Bruce");
user.setUserSex("Male");
user.setUserBirthday(new Date());
user.setUserAddress("Jinan, Shandong");
userDao.updateUserById(user);
}
/** * Test save account */
@Test
public void testSaveUser(a) {
User user = new User();
user.setUserName("Bruce");
user.setUserSex("Male");
user.setUserBirthday(new Date());
user.setUserAddress("Jinan, Shandong");
userDao.saveUser(user);
System.out.println(new Date());
}
/** * Test deletes user */ based on id
@Test
public void testDeleteUserById(a) {
userDao.deleteUserById(59);
}
/** * Test query user by name */
@Test
public void testGetUserByName(a) {
List<User> users = userDao.getUserByName("Bruce");
for(User user : users) { System.out.println(user); }}/** * Get the total number of user entries */
@Test
public void testGetUserTotal(a) {
int userTotal = userDao.getUserTotal();
System.out.println("Total number of users:"+ userTotal); }}Copy the code
The rest of this article focuses on this code, so give it a try
5. About log4j
5.1 introduction of log4j
Log4j is an open source project of Apache. By using Log4j, you can control the destination of log messages to the console, files, GUI components, even the socket server, NT event logger, UNIX Syslog daemon, etc. We can also control the output format of each log; By defining the level of each log message, we can more carefully control the log generation process. The most interesting thing is that these can be configured flexibly through a configuration file without the need to modify the application code.
5.2 Using Log4J
① Configure Maven coordinates and import log4j
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Copy the code
② Write the configuration file
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=E:test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%t] %-5p %30.30c %x - %m\n
Copy the code
More on the use of Log4j will be updated in the future. Here’s a blog log4j configuration that I think is pretty good
MyBatis connection pool and transaction operation
1. Database connection pool overview
Database connection pooling allocates, manages, and releases database connections, and allows applications to reuse an existing database connection rather than create another one. Release database connections whose idle time exceeds the maximum idle time to avoid database connection omissions caused by not releasing database connections. This technique can significantly improve the performance of database operations.
2. Database connection pool in MyBatis
2.1 MyBatis connection pool classification
-
UNPOOLED: data source that does not use connection pooling
-
POOLED: Data source that uses connection pooling
-
JNDI: Data source implemented using JNDI
The structure is as follows:
Inheritance diagram:
MyBatis defines UnpooledDataSource and PooledDataSource classes that implement java.sql.DataSource interface respectively to represent UNPOOLED and POOLED data sources
The UnpooledDataSource class and PooledDataSource implement the java.sql.DataSource interface, and PooledDataSource holds a reference to the UnpooledDataSource. When PooledDataSource needs to create java.sql.Connection instances, UnPooledDataSource does so. PooledDataSource only provides a mechanism for caching Connection pools.
POOLED data sources are commonly used to better manage database connections through a database connection pool.
2.2 Data source configuration in MyBatis
MyBatis creates a DataSource according to the type attribute of
.
Type = “POOLED” : MyBatis creates PooledDataSource instance. Mybatis uses the connection pool in the traditional javax.sql.DataSource specification, which is implemented in Mybatis.
Type = “UNPOOLED” : MyBatis creates an UnpooledDataSource instance. While the javax.sql.DataSource interface is implemented in the traditional way of retrieving connections, the idea of pooling is not used.
Type = “JNDI” : MyBatis looks up the DataSource instance from the JNDI service and returns it. Using the JNDI technology provided by the server to obtain the DataSource object, different servers can get the DataSource is not the same. Note: This is not available unless it is a Web or Maven war project.
2.3 Access to DataSource in MyBatis
MyBatis defines the abstract factory interface to create a DataSource object: Org. Apache. Ibatis. The datasource. DataSourceFactory, through getDataSource () method returns the data source to the datasource
DataSourceFactory source code:
package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
public interface DataSourceFactory {
void setProperties(Properties var1);
DataSource getDataSource(a);
}
Copy the code
MyBatis creates the DataSource instance and places it in the Environment object of the Configuration object for later call.
2.4 Connection obtaining process in MyBatis
MyBatis calls the dataSource object to create the java.sql.Connection object when we need to create the SqlSession object and execute the SQL statement. That is, the creation of the java.SQL. Connection object is delayed until the SQL statement is executed.
PooledDataSource works
3. Transaction control of MyBatis
3.1 Database Transactions
A database transaction is a series of operations performed as a single logical unit of work, all or none of which is an indivisible unit of work.
Features of database transactions
-
Atomicity: All operations in a transaction are indivisible in the database and either complete or not.
-
Consistency: Several transactions executed in parallel, the results of which must be consistent with those executed sequentially in a certain order.
-
Isolation: Transactions execute without interference from other transactions, and the intermediate results of transaction execution must be transparent to other transactions.
-
Durability: For any committed transaction, the system must guarantee that the transaction’s changes to the database are not lost, even if the database fails.
ACID properties of transactions are implemented by relational database systems (DBMSS), which use logging to ensure atomicity, consistency, and persistence of transactions. The log records the updates made by a transaction to the database. If an error occurs during the execution of a transaction, the updates made by the transaction to the database can be revoked based on the log, causing the database to roll back to its initial state before the execution of the transaction.
DBMS uses lock mechanism to realize transaction isolation. When multiple transactions update the same data in the database at the same time, only the transaction holding the lock is allowed to update the data, and the other transactions must wait until the previous transaction releases the lock before the other transactions have a chance to update the data.
Problems that occur when a database is read
① Dirty reads: reads Dirty data.
That is, if uncommitted (and still cached) data from transaction A is read by transaction B, if transaction A fails to roll back, transaction B will read the wrong data.
② Non-repeatable reads: Data cannot be read repeatedly.
For example, transaction A reads the value of price at two places. On the first read, price is 100, and then transaction B changes price to 200; Transaction A reads it again and finds that the price has changed to 200, causing transaction A’s data to be scrambled.
③ Phantom reads: Phantom reads data.
This is similar to non-repeatable reads, which is also a problem of multiple reads being inconsistent in the same transaction. But non-repeatable reads are inconsistent because the data it reads has been changed (such as price) while phantom reads are inconsistent because its conditional data set has been changed.
For example, run the Select account.id where account.name=”Bruce*” command. On the second read, transaction B changed the name of an account from “DD” to “Bruce1”, resulting in seven data fetched.
The emphasis of non-repeatable read is to modify: the same condition, two read values are not the same;
The key of magic reading is to add or delete: the number of records obtained is different between the two reads under the same condition
Isolation level of the database
① ISOLATION_READ_UNCOMMITTED means that a transaction can read data from another uncommitted transaction. Dirty reads, unrepeatable reads, phantom reads (lowest isolation level, high concurrency)
② ISOLATION_READ_COMMITTED means that a transaction can read data only after another transaction commits. Unrepeatable read, phantom read (lock row being read, Oracle default level, on most systems)
③ ISOLATION_REPEATABLE_READ is no longer allowed to modify the data when the data is read (transaction is started), solving the problem of unrepeatable read. Phantom read problem (lock all rows read, MYSQL default level)
⑥ ISOLATION_SERALZABLE is the highest transaction isolation level. At this level, transactions are executed sequentially to avoid dirty reads, unrepeatable reads, and phantom reads. However, this transaction isolation level is inefficient and costly to database performance, and is generally not used. (Lock table)
The transaction isolation level increases from top to bottom. The higher the isolation level, the more data integrity and consistency are ensured. However, the consumption of database performance increases and the concurrent execution efficiency decreases.
The default isolation level for most databases is Read Commited, such as SqlServer and Oracle
For a few databases, the default isolation level is Repeatable Read. For example, MySQL InnoDB
3.2 Transaction control of MyBatis
Mybatis framework is the encapsulation of JDBC, so the transaction control mode of Mybatis framework is also set by JDBC setAutoCommit() method.
MyBatis commits transactions by calling JDBC’s setAutoCommit() method. Commit and rollback transactions through the commit method and rollback method of the sqlsession object.
For more information on connection pooling and transaction control in MyBatis, check out the MyBatis Java API
MyBatis dynamic SQL
1. Introduction to dynamic SQL
Dynamic SQL statement concatenation through MyBatis tags, solve the previous use of JDBC and other similar framework, according to different conditions when the string concatenation SQL statement complex and inconvenient. For example, when concatenating, make sure you don’t forget to add necessary Spaces, and take care to remove the comma from the last column name of the list. Through MyBatis dynamic SQL can be convenient to solve these problems, so that the code becomes more concise and easy to read.
2. Dynamic SQL labels
: The SQL statement is concatenated if the conditions in the tag meet. Otherwise, the SQL statement is not concatenated. The test attribute in the
tag writes the property name of the object. If the object is a wrapped class, use the OGNL expression
< WHERE > : MyBatis inserts a WHERE clause when the element in the tag matches the condition. Also, if the clause begins with AND OR OR, the WHERE element removes those as well.
: allows you to specify a collection that declares the collection item and index variables that can be used inside the element body. It also allows you to specify beginning and ending strings and separators between iterations of collection items. This element also does not mistakenly add extra delimiters.
Note: The dynamic SQL tags listed here are only commonly used, please refer to MyBatis official document: MyBatis dynamic SQL
3. Dynamic SQL usage
Extract SQL statements using the < SQL > tag
<sql id="getUsers">
select * from user
order by birthday desc
</sql>
<! SQL > select * from SQL;
<select id="getUsers" resultMap="userMap">
<include refid="getUsers"></include>
</select>
Copy the code
SQL statement conditional concatenation using < WHERE > tags and
tags
<select id="getUserByCondition" parameterType="cn.bruce.domain.User" resultMap="userMap">
select * from user
<where>
<if test="userName ! = null">
and username like '%${userName}%'
</if>
<if test="userSex ! = null">
and sex = #{userSex}
</if>
<if test="userAddress ! = null">
and address like '%${userAddress}%'
</if>
</where>
order by birthday desc
</select>
Copy the code
The < WHERE > and
tags are used to traverse the query results
<! Query user list based on id set in queryVo
<select id="getUserByIds" parameterType="queryVo" resultMap="userMap">
<include refid="getUsers"></include>
<where>
<if test="ids ! = null and ids.size() > 0">
<foreach collection="ids" open="and id in (" close=")" item="userId" separator=",">
#{userId}
</foreach>
</if>
</where>
</select>
Copy the code
Five, MyBatis multi-table operation
Operation in front of us is for single table operation, the returned result set just the content of the structure of a database table, but the actual development business will inevitably encounter the one-to-many and many-to-many, this situation requires us to MyBatis framework for further study and understanding, also raises the contents of this section, multi-table operation.
1. Perform one-to-many operation for multiple tables
One-to-many: The relationship between users and accounts. One user has multiple accounts, and one account corresponds to only one user
Method 1: Define a special Po class as the output type, which defines all the fields in the SQL query result set.
Method 2: Use resultMap to define specific resultMap query results, add instance objects to the entity class to be associated with the entity class, and write the GET () set() method.
Code examples:
Method two is used here, and the code of times is basically similar to the basic CRUD code above, only the differences are shown.
The User entity class
// Omit the get(), set(), and toString() methods here
public class Account {
private Integer id;
private Integer uid;
private Double money;
The secondary table entity should contain an object reference to the primary table entity
private User user;
}
Copy the code
The Account entity class
// Omit the get(), set(), and toString() methods here
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// One-to-many relational mapping: the primary table entity should contain collection references from the subordinate table entities
private List<Account> accounts;
}
Copy the code
Persistence layer interface mapping file
IUserDao.xml
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bruce.dao.IUserDao">
<resultMap id="userAccountMap" type="user">
<! -- Configure the mapping between user entity classes and columns -->
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<! -- Configure the correspondence between the accounts entity class and the column -->
<collection property="accounts" ofType="account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
</collection>
</resultMap>
<! Select * from user where user = 'user';
<select id="getUsers" resultMap="userAccountMap">
select * from user u left outer join account a on u.id = a.uid
</select>
<! Select * from user where id = 0;
<select id="getUserById" parameterType="int" resultMap="userAccountMap">
select * from user where id = #{userId}
</select>
</mapper>
Copy the code
IAccountDao.xml
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bruce.dao.IAccountDao">
<! Create a resultMap that encapsulates user and account.
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<! Configure one-to-one relationship mapping: encapsulate user content -->
<association property="user" column="uid" javaType="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"/>
</association>
</resultMap>
<! Select * from user where user >
<select id="getAccounts" resultMap="accountUserMap">
select u.*, a.id as aid, a.uid, a.money
from account a, user u
where u.id = a.uid
</select>
<! Select username and address from user where username and address are stored.
<select id="getAccountWithMore" resultType="AccountUser">
select a.*, u.username, u.address
from account a, user u
where a.uid = u.id
</select>
</mapper>
Copy the code
2. Multi-table many-to-many operation
Many-to-many relationship example: Relationships between users and roles. One user corresponds to multiple roles, and one role corresponds to multiple users. The representation of many-to-many relationship in the database requires the use of intermediate tables, so it is necessary to carry out associated query when querying.
Solution and a pair of more similar, in the view of the user table each user and role of the relationship between a one-to-many relationship, the role of each role and user perspective is also a one-to-many relationship between, so it can be users and roles respectively in the corresponding entity class join the role and user array used to store the result set returned by the query.
Sample code:
The User entity class
public class User {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
/** * Many-to-many relationship mapping configuration, the same user corresponds to multiple roles *@return* /
private List<Role> roles;
}
Copy the code
Role entity class
// Omit the get(), set(), and toString() methods here
public class Role {
private Integer roleId;
private String roleName;
private String roleDesc;
/** * Many-to-many relationship mapping configuration, the same role corresponds to multiple users */
private List<User> users;
}
Copy the code
Persistence layer interface mapping file
IUser.xml
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bruce.dao.IUserDao">
<! ResultMap -->
<resultMap id="userMap" type="cn.bruce.domain.User">
<id property="userId" column="id"/>
<result property="userName" column="username"/>
<result property="userBirthday" column="birthday"/>
<result property="userSex" column="sex"/>
<result property="userAddress" column="address"/>
<! -- Configure role set mapping -->
<collection property="roles" ofType="role">
<id property="roleId" column="id"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
</collection>
</resultMap>
<! -- Query all users -->
<select id="getUsers" resultMap="userMap">
select u.*, r.id as rid, r.role_name, r.role_desc from user u
left join user_role ur on u.id = ur.uid
left join role r on ur.rid = r.id
</select>
<! Select * from user by id;
<select id="getUserById" resultType="cn.bruce.domain.User">
select * from user where id = #{userId}
</select>
</mapper>
Copy the code
IRole.xml
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bruce.dao.IRoleDao">
<resultMap id="roleMap" type="Role">
<id property="roleId" column="id"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<collection property="users" ofType="user">
<id property="userId" column="id"/>
<result property="userName" column="username"/>
<result property="userBirthday" column="birthday"/>
<result property="userSex" column="sex"/>
<result property="userAddress" column="address"/>
</collection>
</resultMap>
<! SQL > select * from all roles
<select id="getRoles" resultMap="roleMap">
select u.*, r.id as rid, r.role_name, r.role_desc from role r
left join user_role ur on r.id = ur.rid
left join user u on ur.uid = u.id
</select>
</mapper>
Copy the code
The use of SQL statement query need to use the database table connection, here the use of the left outer join. This chapter covers a lot of database operation related content, about the database operation content will write a blog for detailed explanation, please look forward to. (low low ◡)
JNDI correlation
1. Introduction of JNDI
Java Naming and Directory Interface (JNDI) is a standard Java Naming system Interface provided by SUN Corporation. JNDI provides a unified client API. Administrators map JNDI apis to specific naming and directory systems through the implementation of the JNDI Service Provisioning Interface (SPI) through different access provider interfaces, enabling Java applications to interact with these naming and directory services. Directory services are a natural extension of naming services. The key difference between the two is that objects in a directory service can have names as well as attributes (for example, a user has an email address), whereas objects in a naming service have no attributes.
Cluster JNDI implements high reliability JNDI, which ensures JNDI load balancing and error recovery through clustering of servers. With global sharing, one application server in the cluster guarantees the independence of the local JNDI tree and owns the global JNDI tree. Each application server binds the deployed service object to its local JNDI tree and to a shared global JNDI tree, linking the global JNDI to its own JNDI.
Java Naming and Directory Interface (JNDI) is an APPLICATION programming Interface (API), providing developers with a universal and unified Interface for searching and accessing various Naming and Directory services, similar to JDBC, which is built on the abstraction layer. Now THAT JNDI has become a J2EE standard, all J2EE containers must provide a JNDI service.
Existing directories and services accessible by JNDI are:
DNS, XNam, Novell Directory Service, Lightweight Directory Access Protocol (LDAP), CORBA Object Service, file system, and Windows XP/2000/NT/Me/ 9X registry, RMI, DSML V1 & V2, NIS.
2. Use JNDI
Set up the Maven project to import the relevant JAR packages
<! The Maven coordinates are displayed in the same way as the Maven coordinates.
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
Copy the code
Create a meta-INF directory in the webApp file and write the context. XML configuration file in the directory
<Context>
<Resource>name="jdbc/jndi_mybatis" type="javax.sql.DataSource" auth="Container" maxActive="20" maxWait="10000" maxIdle="5" userName="root" password="123456" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/base_crud? characterEncoding=utf-8"</Resource>
</Context>
Copy the code
Modify mybatis configuration file
<! DOCTYPEconfiguration
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="cn.bruce.domain"/>
</typeAliases>
<! Mysql > configure mysql
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/jndi_mybatis"/>
</dataSource>
</environment>
</environments>
<! -- Specify mapping file -->
<mappers>
<mapper resource="dao/IUserDao.xml"/>
</mappers>
</configuration>
Copy the code
For more information about JNDI and its use, those interested can check out this blog post: JNDI
7, MyBatis cache related
1. Lazy loading of MyBatis
1.1 introduction
Lazy loading refers to loading data only when it is needed and not when it is not needed. Lazy loading is also called lazy loading.
- Advantages: Saves resources. Each query queries a single table first and queries associated tables only when data is required, improving database performance. A single table query consumes much less database performance than an associated query.
- Disadvantages: The timing of associated query is delayed. When a large amount of data needs to be queried, the return time of the query result increases, which may cause a long wait for users and affect user experience.
For example, when querying the one-to-many relationship between a user and an account, you can postpone the account query operation until the account information is required.
1.2 Usage Mode
① Configure lazy loading in the MyBatis configuration file
② In the one-to-many relationship, configure the lazy loading policy in the
< Collection > tag: Used to load the associated collection object
select
Property: Used to specify the SQL statement to query the associated table. Fill in this SQL mapping IDcolumn
Property: Configures the primary key of the query. The parameter source of the SQL statement used to specify the SELECT attribute.
The
tag: used to load the associated single table
select
Property: Used to specify the SQL statement to query the associated table. Fill in this SQL mapping IDcolumn
Property: Configures the primary key of the query. The parameter source of the SQL statement used to specify the SELECT attribute.
Code examples:
Modify MyBatis configuration file
<! DOCTYPEconfiguration
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<! -- Config config file -->
<properties resource="jdbcConfig.properties"></properties>
<! -- Configure parameters -->
<settings>
<! -- Enable lazy loading configuration parameters -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<! -- Configure an alias for the entity class -->
<typeAliases>
<package name="cn.bruce.domain"/>
</typeAliases>
<! -- Configure the environment -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<! Mapping file location -->
<mappers>
<mapper resource="dao/IUserDao.xml"/>
<mapper resource="dao/IAccountDao.xml"/>
</mappers>
</configuration>
Copy the code
Configure the persistence layer mapping file
IUser.xml
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bruce.dao.IUserDao">
<! -- Configure restMap -->
<resultMap id="userMap" type="user">
<id property="userId" column="id"/>
<result property="userName" column="username"/>
<result property="userBirthday" column="birthday"/>
<result property="userSex" column="sex"/>
<result property="userAddress" column="address"/>
<! Set account set mapping for user -->
<collection property="accounts" ofType="account" select="cn.bruce.dao.IAccountDao.getAccountByUid" column="id"/>
</resultMap>
<! Select * from 'all';
<select id="getUsers" resultMap="userMap">
select * from user
</select>
<! Get user name by id -->
<select id="getUserById" parameterType="int" resultType="user">
select * from user where id = #{userId}
</select>
</mapper>
Copy the code
IAccount.xml
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.bruce.dao.IAccountDao">
<! Create a resultMap -->
<resultMap id="accountMap" type="account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<! -- One-to-one relational mapping: configure and encapsulate the contents of the user. Select the contents specified by the select attribute: query the unique identifier of the user. Column specifies the values of the parameters required by the user to query by ID.
<association property="user" column="uid" javaType="user" select="cn.bruce.dao.IUserDao.getUserById"/>
</resultMap>
<! Select * from user where username = ' ';
<select id="getAccounts" resultMap="accountMap">
select * from account
</select>
<! Select * from account where id = 0;
<select id="getAccountByUid" parameterType="int" resultMap="accountMap">
select * from account where uid = #{userId};
</select>
</mapper>
Copy the code
Here only show important code, complete code please check: Mybatis lazy loading
2. Mybatis cache
2.1 introduction
The MyBatis framework, like other persistence layer frameworks, also provides caching policies to reduce the number of database queries and improve performance. The cache in MyBatis is divided into level 1 cache and level 2 cache.
Cache structure diagram:
2.1.1 Level 1 cache in MyBatis
MyBatis level 1 cache is a SqlSession level cache that exists as long as the SQL Session does not execute flush() or close().
When we initiate query for many times, the first query will initiate SQL query, and then Mybatis will cache the query result. In the future, it will not initiate SQL query but directly query data from the cache. The level 1 cache is cleared when the SqlSession modify, delete, add, commit(), and close() methods are called.
Call execution diagram:
- When you query information about user 1 for the first time, check whether the information about user 1 exists in the cache. If not, query the information about the user from the database.
- Get the user information and store the user information in the level 1 cache.
- If sqlSession performs commit (insert, update, or delete), the sqlSession level 1 cache will be cleared. The purpose of this operation is to store the latest information in the cache and avoid dirty reads.
- When querying user information about user 1 for the second time, check whether there is user information about user 1 in the cache. If there is user information about user 1 in the cache, obtain user information from the cache.
2.1.2 Secondary cache in MyBatis
MyBatis level 2 cache is Mapper level cache, multiple SQLsessions to operate the same Mapper mapping SQL statement, multiple SQLsessions can share level 2 cache, level 2 cache is cross-SQLSession.
Cache structure diagram:
After level 2 cache is enabled:
- SqlSession1 to query user information, the queried user information data will be stored in the level 2 cache;
- If SqlSession3 executes the same MAPper mapping and executes the commit, the mapper mapping will be cleared.
- SqlSession2: query the same user information as SqlSession1: query the same user information as SqlSession1;
When using level 2 caching, the cached classes must implement the java.io.Serializable interface so that objects can be stored using serialization.
About the cache part of the code is more, here will not show, detailed code please see: MyBatis cache
MyBatis annotation driver
1. MyBatis
@insert: Implements new operations, equivalent to < Insert > tags
@update: Implements the Update operation, equivalent to the < Update > tag
@delete: Implements the Delete operation, equivalent to the < Delete > tag
@select: implement the query operation, equivalent to the < Select > tag
@result: Encapsulates the Result set, equivalent to < Result > or
tags
- Id attribute: Whether primary key field
- Column property: The name of the column corresponding to the database
- Property: Indicates the name of the property to be configured
- One property: Required
@<One>
annotations(@Result (one = @One) ())
- The MANY attribute: required
@Many
annotations(@Result (many = @Many) ())
@results: Can be used together with @result to encapsulate multiple Result sets. Equivalent to the
tag
ResultMap: Implement encapsulation that references the @Results definition
@ONE: Implements one-to-one result set encapsulation, equivalent to the
tag, used in annotations to specify that a subquery returns a single object.
- Select property: Specifies the SQLMapper for multi-table queries
- FetchType property: overrides global configuration parameters
lazyLoadingEnabled
- Use format:
@Result(column = "", property = "", one = @One(select = ""))
@many: Implements one-to-many result set encapsulation, equivalent to the
- Focus elements are used to deal with one-to-many relationships. The attributes of the mapped Java entity class need to be specified. The javaType of the attributes (typically ArrayList) may not be defined in the annotations.
- Use format:
@Result(property = "", column = "", many = @Many(select = ""))
SelectProvider: Implements dynamic SQL mapping
CacheNameSpace: Annotated cache level 2 application
2. Annotations implement basic CRUD
Use steps:
- Write the entity class corresponding to the database
- Write the data persistence layer interface
- Writing configuration files
Code examples:
Persistence layer interface IUserDao
public interface IUserDao {
/** * Add new user *@param user
*/
@Insert("insert into user (username, birthday, sex, address) values (#{username}, #{birthday}, #{sex}, #{address})")
void saveUser(User user);
/** * Delete user * based on id@param userId
*/
@Delete("delete from user where id = #{userId}")
void deleteUserById(Integer userId);
/** * Modify user information *@param user
*/
@Update("update user set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} where" + " id = #{id}")
void updateUser(User user);
/** * Get all users *@return* /
@Select("select * from user")
List<User> getUsers(a);
/** * Get user * by id@param userId
* @return* /
@Select("select * from user where id = #{userId}")
User getUserById(Integer userId);
/** * query by name *@param userName
* @return* /
@Select("select * from user where username like '%${userName}%'")
List<User> getUserByName(String userName);
/** * Query the total number of users *@return* /
@Select("select count(*) from user")
int getTotalUser(a);
}
Copy the code
Only annotation-related codes are shown here. Please refer to Mybatis annotation CRUD for detailed codes
3. Annotate advanced operations
Use annotations for one-to-many associated queries and lazy cache loading
Writing steps:
- Write the entity class corresponding to the database
- Write the persistence layer interface
- Writing configuration files
Code examples:
The persistence layer interface IUserDao configures the one-to-many relationship
public interface IUserDao {
/** * Query all users *@return* /
@Select("select * from user")
@Results(id = "userMap", value={ @Result(id = true, property = "userId",column = "id"), @Result(property = "userName", column = "username"), @Result(property = "userBirthday", column = "birthday"), @Result(property = "userSex", column = "sex"), @Result(property = "userAddress", column = "address"), @Result(property = "accounts", column = "id", many = @Many(select = "cn.bruce.dao.IAccountDao.getAccountByUid", fetchType = FetchType.LAZY)) })
List<User> getUsers(a);
/** * Get user * by id@param userId
* @return* /
@Select("select * from user where id = #{userId}")
@ResultMap("userMap")
User getUserById(Integer userId);
/** * Fuzzy query by name *@param userName
* @return* /
@Select("select * from user where username like '%${userName}%'")
@ResultMap("userMap")
List<User> getUserByName(String userName);
}
Copy the code
The persistence layer interface IAccountDao is configured with one-to-one relationships
public interface IAccountDao {
/** * get all accounts *@return* /
@Select("select * from account")
@Results(id = "accountMap", value = { @Result(id = true, property = "id", column = "id"), @Result(property = "uid", column = "uid"), @Result(property = "money", column = "money"), @Result(property = "user", column = "uid", one = @One(select = "cn.bruce.dao.IUserDao.getUserById", fetchType = FetchType.EAGER)) })
List<Account> getAccounts(a);
/** * Get account information based on uid *@param userId
* @return* /
@Select("select * from account where uid = #{userId}")
List<Account> getAccountByUid(Integer userId);
}
Copy the code
Only annotation configuration of persistence layer is shown here. For the specific code, please see: Mybatis annotation configuration of one-to-many
Nine, manual implementation of Mybatis framework basic structure
After the above introduction, we have a basic understanding and understanding of MyBatis framework. We can also try to realize some functions of MyBatis framework, such as query related functions. The MyBatis framework is not implemented manually to replace a framework already designed. We don’t know enough about it, nor do we understand many of the design details. The purpose of our manual implementation is to understand the principle of framework parsing execution and the design pattern involved, strengthen the understanding of MyBatis framework, and help us better use the framework.
1. Basic process of MyBatis framework implementation:
2. Basic idea of design
-
Design sqlSession-related classes
- Design the class that creates the factory
- Design the agent factory class
- Design the class that actually creates the Dao object
-
Design configuration Beans
- Design the beans associated with the database connection
- Design beans that execute SQL statements and result types
-
Design file IO read classes — for XML configuration
-
Design custom annotation classes – for annotation configuration
-
Design tool class
- Design a utility class that parses the XML to retrieve SQL statements
- A utility class designed to parse annotations gets the SQL statement
The specific implementation code is more, here will not show, detailed code please see: manual implementation of MyBatis framework basic structure
3. Design patterns applied
3.1 Factory Mode
Factory Pattern is one of the most commonly used design patterns in Java. This type of design pattern is the creation pattern, which provides the best way to create objects.
In factory mode, we create objects without exposing the creation logic to the client, and by using a common interface to point to the newly created objects.
The factory pattern invokes UML diagrams
Implementation code:
// Graphical interface, no specific type specified
public interface Shape {
void draw(a);
}
// Rectangle class to implement the graphical interface
public class Rectangle implements Shape {
@Override
public void draw(a) {
System.out.println("Inside Rectangle::draw() method."); }}// Square class to implement the graphical interface
public class Square implements Shape {
@Override
public void draw(a) {
System.out.println("Inside Square::draw() method."); }}// Circular class to implement the graphical interface
public class Circle implements Shape {
@Override
public void draw(a) {
System.out.println("Inside Circle::draw() method."); }}// Graph factory, used to create graph objects
public class ShapeFactory {
// Use getShape to get an object of the shape type
public Shape getShape(String shapeType){
if(shapeType == null) {return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")) {return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")) {return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")) {return new Square();
}
return null; }}// Test class, test factory mode
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
// Get the Circle object and call its draw method
Shape shape1 = shapeFactory.getShape("CIRCLE");
// Call Circle's draw method
shape1.draw();
// Get the Rectangle object and call its draw method
Shape shape2 = shapeFactory.getShape("RECTANGLE");
// Call the Rectangle draw method
shape2.draw();
// Get the Square object and call its draw method
Shape shape3 = shapeFactory.getShape("SQUARE");
// Call Square's draw methodshape3.draw(); }}Copy the code
3.2 Proxy Mode
Wrap the object with a proxy and replace the original object with that proxy object. Any calls to the original object go through the proxy. The proxy object determines if and when a method call is rolled over to the original object.
3.1.1 Static Proxy
Code examples:
Nike and the cooperative factory jointly produce clothes. Nike does not need to pay attention to the specific production of clothes, but only needs to provide design drawings. The cooperative factory will help Nike complete the production.
interface ClothFactory{
void produceCloth(a);
}
// Proxy class
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth(a) {
System.out.println("Nike makes clothes."); }}/ / the proxy class
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;// instantiate with the proxied class object
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth(a) {
System.out.println("Do some preparatory work on behalf of the factory");
factory.produceCloth();
System.out.println("Do some finishing work for the factory."); }}/ / test
public class StaticProxyTest {
public static void main(String[] args) {
// Create an object of the proxied class
ClothFactory nike = new NikeClothFactory();
// Create objects of the proxy class
ProxyClothFactory proxyClothFactory = newProxyClothFactory(nike); proxyClothFactory.produceCloth(); }}Copy the code
Disadvantages of static proxies:
① The proxy class and the target object class are determined during compilation, which is not conducive to program extension.
② Each proxy class can only serve one interface, so the program development is bound to produce too many proxies.
3.1.2 Dynamic Proxy
Dynamic proxy refers to the method that the client calls other objects through the proxy class and dynamically creates the proxy object of the target class when the program is running.
Advantages over static proxies:
All methods declared in the abstract role (interface) are moved to a centralized method in the calling handler so that we can handle many methods more flexibly and uniformly.
Dynamic proxy implementation:
- Create an implementation interface
InvocationHandler
Class, which must implement the Invoke method to complete the specific operation of the proxy. - Create proxied classes and interfaces
- Static method by Proxy
newProxyInstance(ClassLoader loader, Class<? >... interface, InvocationHandler h)
Create an interface proxy - Invoke a proxied class method through an instance of the proxied class
Code implementation:
interface Human {
String getBelief(a);
void eat(String food);
}
// Proxy class
class SuperMan implements Human {
@Override
public String getBelief(a) {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("I like eat "+ food); }}/* What problems need to be solved to implement dynamic proxy? Problem 1: how to dynamically create a proxy class and its object based on the proxy class loaded in memory. Problem 2: how to dynamically call a method of the same name in the proxied class when calling method A from the object of the proxied class. * /
// Create a class that extends the InvocationHandler interface
class MyInvocationHanlder implements InvocationHandler {
private Object obj;// Assign using the object of the proxied class
public void bind(Object obj) {
this.obj = obj;
}
// Invoke () when invoking method A from a class object, the following method is automatically invoked: invoke()
// The function of method A to be executed by the proxied class is declared in invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method: the method called by the proxy class object
//obj: object of the proxied class
Object returnValue = method.invoke(obj, args);
// The return value of the above method is used as the return value of invoke() in the current class.
returnreturnValue; }}class ProxyFactory {
// Call this method to return an object of the proxy class. Problem One
public static Object getProxyInstance(Object obj) {
MyInvocationHanlder hanlder = new MyInvocationHanlder();
hanlder.bind(obj);
returnProxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hanlder); }}// Test the dynamic proxy
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance: object of the proxy class
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
// When a method is called from a proxy class object, the method of the same name in the proxy class is automatically called
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("Hot pot"); }}Copy the code
There’s a lot more to write about in the design patterns section, so stay tuned for more details on the blog. (. ^ del ^)