Introduction to the
What is Mybatis
From the official website:
-
MyBatis is an excellent persistence layer framework that supports custom SQL, stored procedures, and advanced mapping.
-
MyBatis eliminates almost all of the JDBC code and the work of setting parameters and fetching result sets.
-
MyBatis can configure and map primitive types, interfaces, and Java POJOs (Plain Old Java Objects) to records in the database via simple XML or annotations.
From Baidu Encyclopedia:
-
MyBatis is an open source project of Apache called iBatis. In 2010, this project was migrated to Google Code by Apache Software Foundation and renamed as MyBatis. Migrated to Github in November 2013.
-
The term iBATIS comes from the combination of “Internet” and “Abatis”. IBATIS is a Java-based persistence layer framework. IBATIS provides persistence layer frameworks including SQL Maps and Data Access Objects (DAOs)
persistence
- Memory is breakpoint – free
- The data needs to be stored in a database (JDBC) or a file (IO)
The persistence layer
Dao layer, Service layer, Controller layer
- The block of code that does the persistence
- The boundaries are very clear
Access to Mybatis
-
Maven
<! -- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> Copy the code
-
Github
- Github.com/mybatis/myb…
The first Mybatis program
Environment set up
-
Java
-
Maven
-
Mysql
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL, `username` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL.PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1.'volcano'.'123321'); INSERT INTO `user` VALUES (2.'candashuai'.'123123'); INSERT INTO `user` VALUES (3.'Joe'.'123456'); SET FOREIGN_KEY_CHECKS = 1; Copy the code
Create a project
-
Create a project
-
Delete SRC directory
-
Import dependence
<dependencies>
<! -- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<! -- Mysql driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<! -- Unit tests -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
Copy the code
-
Create a new Module that can use the main project dependencies
Begin to use
Writing configuration files
Create a new mybatis-config. XML file under SRC /main/resources. When using, delete all Chinese comments, otherwise an error will be reported
<! DOCTYPEconfiguration
PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<! -- Database driver -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<! For database connection, ampersand should be & Instead, mysql8 and above need to add time zone configuration -->
<property name="url" value="jdbc:mysql://localhost:3306/smbms? useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<! -- Username and password -->
<property name="username" value="root"/>
<property name="password" value="1q2w3e4r5t"/>
</dataSource>
</environment>
</environments>
<! Mapper = Mapper = Mapper = Mapper -->
<mappers>
<mapper resource="com/volcano/dao/UserMapper.xml"/>
</mappers>
</configuration>
Copy the code
Write utility classes
In SRC/main/Java/com/volcano/utils MybatisUtils new Java
package com.volcano.utils;
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 java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory SqlSessionFactory;
static {
try {
// Write three sentences of code into a tool class
// Change this path to the path of your configuration file, which is in the resources directory by default
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch(IOException e) { e.printStackTrace(); }}public static SqlSession getSqlSession(a){
//true Automatically commits transactions. The default value is false
returnSqlSessionFactory.openSession(); }}Copy the code
Creating an entity Class
In the SRC/main/Java/com/volcano/pojo created under User. Java
package com.volcano.pojo;
public class User {
private int id;
private String username;
private String password;
public User(a) {}public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
@Override
public String toString(a) {
return "User{" +
"id=" + id +
", username='" + username + '\' ' +
", password='" + password + '\' ' +
'} ';
}
public void setId(int id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public int getId(a) {
return id;
}
public String getUsername(a) {
return username;
}
public String getPassword(a) {
returnpassword; }}Copy the code
Create Dao
In the SRC/main/Java/com/volcano/dao created under UserDao. Java and UserMapper. XML
package com.volcano.dao;
import com.volcano.pojo.User;
import java.util.List;
// Subsequent DAOs will be renamed Mapper
public interface UserDao {
// Define a method that returns a list of users
List<User> getUserList(a);
}
Copy the code
<! DOCTYPEmapper
PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<! Bind the namespace to UserDao to find the method.
<mapper namespace="com.volcano.dao.UserDao">
<! -- id is the method name and the corresponding resultType in UserDao is the return type -->
<select id="getUserList" resultType="com.volcano.pojo.User">
select * from mybatis.user
</select>
</mapper>
Copy the code
test
Create userdaotest.java under SRC /test/ Java with the same package structure as in main (optional)
package com.volcano.dao;
import com.volcano.pojo.User;
import com.volcano.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserDaoTest {
@Test
public void test(a){
//1. Obtain the SqlSession object
SqlSession sqlSession = MybatisUtils.getSqlSession();
// Method 1: getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
// Method 2: Old methods are not recommended
// List
userList = sqlSession.selectList("com.volcano.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
// It is best to close in finallysqlSession.close(); }}Copy the code
The project structure
To realize the CRUD
Change the previous UserDao to UserMapper and modify all references.
-
Modify UserMapper. Java
package com.volcano.dao; import com.volcano.pojo.User; import java.util.List; public interface UserMapper { // Define a method that returns a list of users List<User> getUserList(a); // Query users by id User getUserById(int id); // Add a user int insertUser(User user); // Delete the user int deleteUser(int id); // Modify user information int updateUser(int id); } Copy the code
-
Modify UserMapper. XML
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <! Bind the namespace to UserDao to find the method. <mapper namespace="com.volcano.dao.UserMapper"> <! -- id is the method name and the corresponding resultType in UserDao is the return type --> <select id="getUserList" resultType="com.volcano.pojo.User"> select * from mybatis.user </select> <! -- parameterType is int, getUserById(int id) parameterType --> <! GetUserById (int id) --> <select id="getUserById" resultType="com.volcano.pojo.User" parameterType="int"> select * from mybatis.user where id = #{id} </select> <! SQL > select * from User; select * from User; <insert id="insertUser" parameterType="com.volcano.pojo.User"> insert into mybatis.user values (#{id},#{username},#{password}) </insert> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id} </delete> <update id="updateUser" parameterType="int"> update `user` set username = "candashuai222",password="999999" where id = #{id} </update> </mapper> Copy the code
-
test
package com.volcano.dao; import com.volcano.pojo.User; import com.volcano.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void test(a){ //1. Obtain the SqlSession object SqlSession sqlSession = MybatisUtils.getSqlSession(); // Method 1: getMapper UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); // Method 2: Old methods are not recommended // List
userList = sqlSession.selectList("com.volcano.dao.UserDao.getUserList"); for (User user : userList) { System.out.println(user); } // It is best to close in finally sqlSession.close(); } @Test public void testSelect(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); } @Test public void testInsert(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); // Pass true to set the default committed transaction to true, so you don't have to manually sqlsession.mit () below //SqlSession sqlSession = MybatisUtils.getSqlSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.insertUser(new User(4."Jack Chen"."666666")); // Commit the transaction to take effect sqlSession.commit(); sqlSession.close(); } @Test public void testDelete(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(3); sqlSession.commit(); sqlSession.close(); } @Test public void testUpdate(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(2); sqlSession.commit(); sqlSession.close(); }}Copy the code
Possible problems
-
Do not mismatch labels
-
Mapper is bound to mybatis-config. XML
-
When the project runs the package, the XML file is not included. Modify the configuration in pom.xml and put it in pom.xml of the main project
<build> <resources> <resource> <! Allow SRC /main/resources to include file types in includes <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build> Copy the code
The use of the Map
When the argument is an entity-class object, it is easy to instantiate if the entity-class has few attributes. If the entity-class has many attributes, it is complicated to instantiate an object because constructors require many parameters, whereas some methods do not.
-
UserMapper.java
// Add user (Map) int insertUserMap(Map map); Copy the code
-
UserMapper.xml
<! SQL statement (map); SQL statement (map); <insert id="insertUserMap" parameterType="map"> insert into mybatis.user values (#{uid},#{uname},#{pwd}) </insert> Copy the code
-
TestUserDao.java
@Test public void testInsertMap(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); // The key in the map corresponds to the placeholder in the XML file map.put("uid".5); map.put("uname"."Map5"); map.put("pwd"."555555"); mapper.insertUserMap(map); sqlSession.commit(); sqlSession.close(); } Copy the code
Configure the parsing
- Mybatis -config.xml, may not use this name (official recommendation)
- Configuration structure (== You must configure == in sequence)
- Configuration
- Properties
- Settings
- typeAliases
- typeHandlers
- objectFactory
- Plugins
- Environments
- Environment (environment variable)
- transactionManager
- A dataSource
- Environment (environment variable)
- DatabaseIdProvider (Database vendor Identity)
- Mappers (mapper)
- Configuration
properties
Purpose: Reference external configuration properties.
The instance
-
Create a new db.properties file under Resources
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=123456 Copy the code
-
Referenced in mybatis-config.xml
<! DOCTYPEconfiguration PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <! -- First in order --> <! -- Case 1: No additional configuration parameters can be directly self-closed --> <properties resource="db.properties"/> <! -- Case 2: additional configuration required (db.properties password deleted)--> <! --<properties resource="db.properties">--> <! --<property name="password" value="1q2w3e4r5t"/>--> <! --</properties>--> <! -- Case 3: The extra parameter is the same as the external parameter, and the external parameter overwrites the extra parameter --> <! --<properties resource="db.properties">--> <! -- If the external parameters are the same, use external --> <! --<property name="password" value="5555"/>--> <! --</properties>--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <! ${} get the properties in the properties file --> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/volcano/mapper/UserMapper.xml"/> </mappers> </configuration> Copy the code
settings
These are very important tuning Settings in MyBatis, and they change the runtime behavior of MyBatis.
Keep the following three in mind and go back to the official documentation for others:
Set the name | describe | Valid values | The default value |
---|---|---|---|
cacheEnabled | Globally turn on or off any caches that have been configured in all mapper profiles. | true | false | true |
lazyLoadingEnabled | Global switch of lazy loading. When enabled, all associated objects are lazily loaded. You can set this parameter in a specific associationfetchType Property to override the on/off state of the item. |
true | false | false |
logImpl | Specify the specific implementation of logging used by MyBatis. If not specified, it will be automatically found. | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Is not set |
typeAliases
A type alias sets an abbreviated name for a Java type. It is only used for XML configuration and is intended to reduce redundant fully qualified class name writing.
<typeAliases>
<! Com.volcane.pojo. User in XML file can be replaced by User -->
<! --<typeAlias type="com.volcano.pojo.User" alias="User"/>-->
<! -- Set aliases for all entity classes in the entire package. Aliases are lowercase.
<! Use @alias ("DIY") to set the Alias on the entity class -->
<package name="com.volcano.pojo"/>
</typeAliases>
Copy the code
// annotations specify an alias (when specifying an alias for the entire package)
@Alias("User")
public class User {... }Copy the code
<! -- resultType in usermapper.xml can be specified as an alias -->
<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
Copy the code
environments
MyBatis can be configured to accommodate a variety of environments, such as development, test, and production environments that require different configurations.
Keep in mind, though: although multiple environments can be configured, only one environment can be selected per SqlSessionFactory instance.
<! -- id specifies the selected environment -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="oracle">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
Copy the code
TransactionManager transactionManager
- JDBC – This configuration directly uses JDBC’s commit and rollback facilities, which rely on connections obtained from data sources to manage transaction scopes.
- MANAGED – This configuration does little.
The data source dataSource
- POOLED – Uses database connection pools
- UNPOOLED — Database connection pooling is not used
- JNDI
mappers
Mapper maps custom mapper files.
-
A:
<! -- Use a resource reference relative to the classpath --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers> Copy the code
-
Method 2: Not recommended
<! -- Use fully qualified resource locator (URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers> Copy the code
-
Method 3: The XML file must be in the same directory
<! Implement the fully qualified class name of the class using the mapper interface <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers> Copy the code
-
Method 4: Note the same as method 3
<! Register all mapper interface implementations as mapper <mappers> <package name="org.mybatis.builder"/> </mappers> Copy the code
Extension: If you want to separate mapper. Java and mapper. XML, you can put the same package name in Java and Resources respectively, and still package in the same directory.
Other configuration
- typeHandlers
- objectFactory
- Plugins
- MyBatis Generator Core
- MyBatis Plus
- Database vendor identity (databaseIdProvider)
- Execute different statements depending on the database vendor
Life cycle and scope
-
SqlSessionFactoryBuilder
- This class can be instantiated, used, and discarded, and is no longer needed once the SqlSessionFactory has been created
- The best scope is the method scope
-
SqlSessionFactory
- Once created, it should exist for the duration of the application, and there is no reason to throw it away or recreate another instance
- Do not create the application multiple times while it is running
- The best scope is the application scope
- Use singleton pattern or static singleton pattern
-
SqlSession
- SqlSession instances are not thread-safe and therefore cannot be shared
- The best scope is the request or method scope
- Be sure to turn it off after each use
ResultMap
A null query can occur when the field name of a database table is inconsistent with the attribute name in the entity class.
Id username Password User Table field IDen uname PWD user Entity class attributeCopy the code
Solution:
-
Select id as iden from user;
-
Use ResultMap in usermapper.xml
<! -- id specifies the name of the entity class --> <resultMap id="UserMap" type="User"> <! Property Specifies the property name of the entity class --> <id column="id" property="iden"/> <id column="username" property="uname"/> <id column="password" property="pwd"/> </resultMap> <! -- resultMap Specifies the ID of the resultMap. <select id="getUserById" resultMap="UserMap" parameterType="int"> select * from mybatis.user where id = #{id} </select> Copy the code
The log
Logs show the execution steps and process of programs in detail, improving the troubleshooting ability.
Used methods: SOUT, DEBUG, etc
Log factory
The Settings property in Mybatis uses logImpl to specify the logging implementation. Name and value must avoid Spaces.
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Copy the code
- SLF4J
- LOG4J LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
Logging effect of STDOUT_LOGGING
Log4j
Unlike STDOUT_LOGGING, Log4j needs to be packaged before it can be used.
- Log4j is an open source project from Apache that can specify the location of log output to the console, files, or even GUI components
- You can control the output format of each log
- Define the level of each log message
- These can be configured flexibly through a configuration file
configuration
-
Import packages
<! -- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> Copy the code
-
Create log4j.properties under Resources
Output DEBUG logs to the console and file destinations. Console and file are defined in the code below log4j.rootLogger=DEBUG,console,file Settings for console output log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n # File output Settings log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/volcano.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n # Log output level log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG Copy the code
-
Configure log4j to implement Mybatis logging
<settings> <setting name="logImpl" value="LOG4J"/> </settings> Copy the code
-
Run: just a few more prefixes
Simple to use
package com.volcano.mapper;
import org.apache.log4j.Logger;
import org.junit.Test;
public class MapperTest {
// Get the log object with the current class
// The package to import is org.apache.log4j.logger
static Logger logger = Logger.getLogger(MapperTest.class);
@Test
public void testLog4j(a){
logger.info("info:testLog4j");
logger.debug("debug:testLog4j");
logger.error("error:testLog4j"); }}Copy the code
[com.volcano.mapper.MapperTest]-info:testLog4j
[com.volcano.mapper.MapperTest]-debug:testLog4j
[com.volcano.mapper.MapperTest]-error:testLog4j
Copy the code
Log4j.appender.file. file =./log/volcano.log
So the above will also be appended to the log file in the log folder of the current project root
paging
Limit to realize
-
UserMapper.java
List<User> getUsersByLimit(Map<String,Integer> limit); Copy the code
-
UserMapper.xml
<! Mybatis = Mybatis = Mybatis <select id="getUsersByLimit" parameterType="map" resultMap="UserMap"> select * from mybatis.user limit #{start},#{pageSize} </select> Copy the code
-
MapperTest.java
@Test public void testLimit(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Integer> map = new HashMap<String, Integer>(); map.put("start".0); map.put("pageSize".2); List<User> userList = mapper.getUsersByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); } Copy the code
RowBounds implementation
Limit is an IMPLEMENTATION based on Sql statements. RowBounds is a Java object-oriented implementation that is not as efficient as Sql. It is not recommended and may be used to maintain old projects.
RowBounds simply says, query all the data and truncate it.
-
UserMapper.java
List<User> getUserByRowBounds(a); Copy the code
-
UserMapper.xml
<select id="getUserByRowBounds" resultMap="UserMap"> select * from mybatis.user </select> Copy the code
-
MapperTest.java
@Test public void testRowBounds(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); RowBounds rowBounds = new RowBounds(1.2); // When you look at this long code, you can see why this method is not recommended List<User> userList = sqlSession.selectList("com.volcano.mapper.UserMapper.getUserByRowBounds".null, rowBounds); for (User user : userList) { System.out.println(user); } sqlSession.close(); } Copy the code
Paging plug-in
Mybatis PageHelper
Development with annotations
-
UserMapper.java
@Select("select * from user") List<User> getUserList(a); // Query user by id and name, use the annotation Param for multiple parameters, single can be added or not (recommended) // Primitive and String types can use Param, but reference types do not @Select("select * from user where id = #{uid} and username = #{uname}") User getUserByIdAndName(@Param("uid") int id,@Param("uname") String name); Copy the code
-
mybatis-config.xml
<mappers> <! -- Specify class instead of XML --> <mapper class="com.volcano.mapper.UserMapper"/> </mappers> Copy the code
-
TestMapper.java
@Test public void testAnnotation(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } User user = mapper.getUserByIdAndName(1."volcano"); System.out.println(user); sqlSession.close(); } Copy the code
Disadvantages: Cannot be used for complex SQL queries, such as objects returned, ResultMap cannot be used in annotations.
Development principle: Simple queries use annotations, complex queries still use XML file configuration. Mappers can configure XML and class at the same time in mybatis-config.xml
Essence: Through reflection
Bottom layer: dynamic proxy
Lombok
Tools that use annotations to simplify the creation of entity classes.
-
IDEA Install Lombok plug-in, if yes, do not need (restart IDEA as prompted)
-
Import jar packages (using Maven here)
<! -- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> Copy the code
-
Start by adding @data annotations to entity classes
Commonly used annotations
// All comments
@Getter
@Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@RequiredArgsConstructor
@NoArgsConstructor
@Log
@Log4j
@Log4j2
@Slf4j
@XSlf4j
@CommonsLog
@JBossLog
@Flogger
@CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Copy the code
Commonly used are:
- @Data
- Add to entity classes that automatically generate getters and setters for all attributes, no-argument constructors, equals(), toString(), hashCode()
- @Getter/@Setter
- For entity classes, generate getters/setters for all properties in the class
- Used on a property to generate a getter/setter for that property
- @ToString/@EqualsAndHashCode
- Generate corresponding methods
conclusion
Use less, team use can use, not your own.
Complex query result processing
The previous queries are all simple queries, and the attributes in the entity class are all strings or basic types, which only use the result or ID in the resultMap to complete the result mapping. However, in projects, it is often encountered that the attribute of one object is another object. The mapping in this case requires association and Collection.
Here we have complex queries, one-to-many and many-to-one.
Environment set up
Physical relationship: teacher-student relationship
- Teacher – > Student (one to many)
- Student — > Teacher (many to one)
Database design
Do not run in batches. If you run too fast, you may start inserting data before the table is created
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL.PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1.'Teacher Qin');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL.PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1'.'Ming'.'1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2'.'little red'.'1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3'.'zhang'.'1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4'.'xiao li'.'1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5'.'wang'.'1');
Copy the code
The project structure
The source code in these files has been noted before, but here is the entity class code
// The constructor and getter/setter are omitted
public class Student {
private int id;
private String name;
// teacher is an entity class that needs to be queried
private Teacher teacher;
}
Copy the code
Unprocessed complex queries
In this example, students can query the corresponding teacher.
-
StudentMapper.java
List<Student> getStudentList0(a); Copy the code
-
StudentMapper.xml
<select id="getStudentList0" resultType="com.volcano.pojo.Student"> select * from mybatis.student </select> Copy the code
-
test
@Test public void test01(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.getStudentList0(); for (Student student : studentList) { System.out.println(student); } sqlSession.close(); } Copy the code
-
The results of
For one more
The above problem occurs because a ResultMap is not properly mapped.
-
StudentMapper.java
List<Student> getStudentList1(a); List<Student> getStudentList2(a); Copy the code
-
StudentMapper.xml
<! -- query by query --> <select id="getStudentList1" resultMap="StudentAndTeacher"> select * from mybatis.student </select> <! Select * from Student where Student = Student; <resultMap id="StudentAndTeacher" type="com.volcano.pojo.Student"> <result column="id" property="id"/> <result column="name" property="name"/> <! -- Many to one --> <! -- subquery = subquery --> <association column="tid" property="teacher" javaType="com.volcano.pojo.Teacher" select="getTeacher"/> </resultMap> <! -- Subquery --> <select id="getTeacher" resultType="com.volcano.pojo.Teacher"> select * from mybatis.teacher where id = #{tid} </select> Copy the code
<! -- nested by result --> <! Sid sname TID tname sid sname tid tname <select id="getStudentList2" resultMap="StudentAndTeacher2"> select s.id sid, s.name sname, t.id tid,t.name tname from mybatis.student s, mybatis.teacher t where s.tid=t.id </select> <resultMap id="StudentAndTeacher2" type="com.volcano.pojo.Student"> <result column="sid" property="id"/> <result column="sname" property="name"/> <! Tid tname = Teacher --> <association property="teacher" javaType="com.volcano.pojo.Teacher"> <result column="tid" property="id"/> <result column="tname" property="name"/> </association> </resultMap> Copy the code
-
Test (same result)
More than a pair of
New project (module), unchanged except Student and Teacher entity class changes, xxxmapper. Java/XML empty.
public class Student {
private int id;
private String name;
/ / to dar
private int tid;
}
Copy the code
public class Teacher {
private int id;
private String name;
// A pair of multiple students
private List<Student> students;
}
Copy the code
-
TeacherMapper.java
Teacher getTeacherById(@Param("tid") int id); Teacher getTeacherById2(@Param("tid") int id); Copy the code
-
TeacherMapper.xml
<! -- Query nested --> <select id="getTeacherById" resultMap="TeacherAndStudents"> select * from mybatis.teacher where id = #{tid} </select> <resultMap id="TeacherAndStudents" type="com.volcano.pojo.Teacher"> <result column="id" property="id"/> <result column="name" property="name"/> <! -- One-to-many use collection column to pass its own ID to subquery --> <! -- ofType = E in List<E> <collection column="id" javaType="List" property="students" ofType="Student" select="getStudentByTeacher"/> </resultMap> <select id="getStudentByTeacher" resultType="Student"> select * from mybatis.student where tid = #{id} </select> Copy the code
<! -- nested by result --> <select id="getTeacherById2" resultMap="TeacherAndStudents2"> select t.id tid, t.name tname, s.id sid, s.name sname,s.tid stid from mybatis.teacher t,mybatis.student s where s.tid = t.id and t.id = #{tid} </select> <resultMap id="TeacherAndStudents2" type="com.volcano.pojo.Teacher"> <result column="tid" property="id"/> <result column="tname" property="name"/> <collection property="students" ofType="com.volcano.pojo.Student"> <result column="sid" property="id"/> <result column="sname" property="name"/> <result column="stid" property="tid"/> </collection> </resultMap> Copy the code
-
Test (same results, single line too long, text representation)
The Teacher {id = 1, name = 'qin Teacher, students = [Student {id = 1, name =' Ming ', tid = 1}, Student {id = 2, name = 'red', tid = 1}, Student {id = 3, Name = 'joba chamberlain, tid = 1}, Student {id = 4, name =' xiao li ', tid = 1}, Student {id = 5, name = 'wang', tid = 1}]}Copy the code
conclusion
- For one more
- association
- More than a pair of
- collection
- JavaType: The data type that is actually returned
- OfType: generics
- Nesting by result is preferred
- Tuning SQL is the focus
Dynamic SQL
Dynamic SQL is one of the powerful features of MyBatis. If you’ve ever used JDBC or a similar framework, you can understand how painful it can be to concatenate SQL statements based on different conditions, such as making sure you don’t forget to add the necessary whitespace and removing the comma from the last column name of the list. With dynamic SQL, you can get rid of this pain completely.
The so-called dynamic SQL, the essence is still SQL statements, but we can execute a logical code at the SQL level.
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
Environment set up
-
The database table
CREATE TABLE `blog`( id varchar(50) NOT NULL COMMENT'blog id', title varchar(100) NOT NULL COMMENT 'Blog title', author varchar(30) NOT NULL COMMENT 'Blogger', create_time datetime NOT NULL COMMENT 'Creation time', views int(30) NOT NULL COMMENT 'Views' )ENGINE=InnoDB DEFAULT CHARSET=utf8 Copy the code
-
The project structure
-
IDUtils.java
import java.util.UUID; public class IDUtils { public static String getId(a){ Universally Unique identifiers return UUID.randomUUID().toString().replace("-".""); }}Copy the code
-
Blog.java
public class Blog { private String id; private String title; private String author; // Set mapUnderscoreToCamelCase in mybatis-config. XML // use java.util for Date private Date createTime; private int views; } Copy the code
-
BlogMapper.java
void insertBlog(Blog blog); Copy the code
-
BlogMapper.xml
<insert id="insertBlog" parameterType="com.volcano.pojo.Blog"> insert into mybatis.blog(id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views}) </insert> Copy the code
-
Mytest.java, insert more data yourself
@Test public void test01(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); // Insert 30 pieces of data for (int i=0; i<30; i++){ mapper.insertBlog(new Blog(IDUtils.getId(),"Title"+i,"volcano_"+i/10.new Date(),i*20)); } sqlSession.commit(); sqlSession.close(); } Copy the code
if
You can use if to determine whether a concatenated Sql statement is needed.
-
BlogMapper.java
List<Blog> getBlogIf(Map map); Copy the code
-
BlogMapper.xml
<select id="getBlogIf" parameterType="map" resultType="blog">Select * from mybatis. Blog where 1=1 select * from mybatis. Blog where 1=1 select * from mybatis<if test="title ! = null"> and title = #{title} </if> <if test="author ! = null"> and author = #{author} </if> </select> Copy the code
-
test
@Test public void test02(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); //map store condition, null search all HashMap map = new HashMap(); map.put("author"."volcano_1"); List<Blog> blogList = mapper.getBlogIf(map); for (Blog blog : blogList) { System.out.println(blog); } sqlSession.close(); } Copy the code
where
In the IF example, in order to successfully concatenate the condition, we add a statement at the beginning of the condition where 1=1. It is not safe to write this statement, but IF we do not concatenate the condition, the following situation will occur:
SELECT * FROM BLOG
WHERE
Copy the code
SELECT * FROM BLOG
WHERE
AND title like'someTitle'Copy the code
MyBatis has a simple solution that works for most scenarios. In other scenarios, you can customize it to fit your needs.
The WHERE element inserts the “where” clause only if the child element returns anything. Also, if the clause begins with “AND” OR “OR,” the WHERE element removes those as well.
-
transform
<select id="getBlogIf" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <if test="title ! = null"> title = #{title} </if> <if test="author ! = null"> and author = #{author} </if> </where> </select> Copy the code
Choose, when, or otherwise
Sometimes, we don’t want to use all of the conditions, but just choose one of several conditions to use. For this, MyBatis provides the Choose element, which is a bit like the Switch statement in Java. (Built-in break switch)
Choose – switch
When – case
Otherw – the default
-
BlogMapper.java
List<Blog> getBlogChoose(Map map); Copy the code
-
BlogMapper.xml
<select id="getBlogChoose" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <choose> <when test="author ! = null"> author = #{author} </when> <when test="title ! = null"> title = #{title} </when> <when test="views ! = null"> views > #{views} </when> <otherwise>The idea here is that if users search for nothing, give some of the most visited blogs views > 500</otherwise> </choose> </where> </select> Copy the code
-
test
Trim, set
A similar solution for dynamically updated statements is called SET. The set element can be used to dynamically contain columns that need to be updated, ignoring other columns that are not updated.
The set element inserts the set keyword dynamically at the beginning of the line and removes additional commas that are introduced when conditional statements are used to assign values to columns.
-
BlogMapper.java
void updateBlog(Map map); Copy the code
-
BlogMapper.xml
<update id="updateBlog" parameterType="map"> update mybatis.blog <set> <if test="title ! = null"> title = #{title}, </if> <if test="author ! = null"> author = #{author}, </if> <if test="views ! = null"> views = #{views}, </if> </set> <where> <choose> <when test="id ! = null"> id = #{id} </when> <otherwise>1 = 2 -- no id allowed to modify data</otherwise> </choose> </where> </update> Copy the code
-
test
@Test public void test04(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); //map stores conditions and modifies the data matching the id HashMap map = new HashMap(); map.put("id"."eb7cc29b63164cf0922ded8c4e67a924"); map.put("author"."vip_9999"); map.put("title"."Title 1 -- VIP Only Enjoy small tail"); mapper.updateBlog(map); sqlSession.commit(); sqlSession.close(); } Copy the code
In Mybatis, you can use the trim element to set where and set to achieve the same effect.
<! Where -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">.</trim>
<! -- set equivalent implementation -->
<trim prefix="SET" suffixOverrides=",">.</trim>
<! -- prefix: prefixOverrides: delete suffixOverrides: delete suffixOverrides -->
Copy the code
foreach
Another common use of dynamic SQL is traversal of collections (especially when building IN conditional statements).
-
BlogMapper.java
List<Blog> getBlogInAuthors(Map map); Copy the code
-
BlogMapper.xml
<select id="getBlogInAuthors" parameterType="map" resultType="com.volcano.pojo.Blog"> select * from mybatis.blog <where> <foreach collection="authors" open="author in (" separator="," close=")" item="author" index="index"> #{author} </foreach> </where> </select> Copy the code
-
test
@Test public void test05(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); ArrayList<String> authors = new ArrayList<String>(); authors.add("volcano_1"); HashMap map = new HashMap(); map.put("authors",authors); List<Blog> blogList = mapper.getBlogInAuthors(map); for (Blog blog : blogList) { System.out.println(blog); } sqlSession.close(); } Copy the code
SQL fragment
To put it bluntly, the SQL statements that can be reused are extracted as a common part and moved wherever needed.
-
Extract the common part of THE SQL
<sql id="if-title-author"> <if test="title ! = null"> title = #{title}, </if> <if test="author ! = null"> author = #{author}, </if> </sql> Copy the code
-
References include
<update id="updateBlog" parameterType="map"> update mybatis.blog <set> <include refid="if-title-author"/> <if test="views ! = null"> views = #{views}, </if> </set> <where> <choose> <when test="id ! = null"> id = #{id} </when> <otherwise> 1 = 2 </otherwise> </choose> </where> </update> Copy the code
The cache
Introduction to the
-
What is a Cache?
- Temporary data exists in memory.
- By storing the data frequently queried by users in the cache (memory), users can query data from the cache instead of from disk (relational database data files), thus improving the query efficiency and solving the performance problems of high concurrency system
-
Why cache?
- Reduce the number of interactions with the database, reduce system overhead, improve system efficiency.
-
What kind of data can be cached?
- Data that is frequently queried and infrequently changed.
Mybatis cache
MyBatis includes a very powerful query caching feature that makes it very easy to customize and configure the cache. Caching can greatly improve query efficiency.
MyBatis system defines two levels of cache by default: level 1 cache and level 2 cache
-
By default, only level 1 caching is enabled. (SqlSession-level cache, also called local cache, life cycle: getSession() — close())
-
Level 2 caching needs to be manually enabled and configured. It is namespace-level caching.
-
To improve scalability, MyBatis defines Cache interface Cache. We can customize the level 2 Cache by implementing the Cache interface
Level 1 cache
Level 1 cache is also called local cache:
- SqlSession The data queried during the same session with the database is stored in the local cache.
- In the future, if you need to retrieve the same data from the cache, you don’t have to query the database.
The sample
@Test
public void testCache01(a){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
// Get the same data twice
Blog blog = mapper.getBlogById("eb7cc29b63164cf0922ded8c4e67a924");
System.out.println(blog);
blog = mapper.getBlogById("eb7cc29b63164cf0922ded8c4e67a924");
System.out.println(blog);
sqlSession.close();
}
Copy the code
Manually clear cache
@Test
public void testCache01(a){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
// Get the same data twice
Blog blog = mapper.getBlogById("eb7cc29b63164cf0922ded8c4e67a924");
System.out.println(blog);
sqlSession.clearCache();
blog = mapper.getBlogById("eb7cc29b63164cf0922ded8c4e67a924");
System.out.println(blog);
sqlSession.close();
}
Copy the code
The second level cache
- Level 2 cache is also called global cache, level 1 cache scope is too low, so was born level 2 cache
- Namespace-level caching, one namespace for each level 2 cache
- Working mechanism
- A session queries a piece of data, and the data is placed in the level 1 cache for the current session
- If the current session is closed, the level 1 cache for that session is gone. But what we want is for the session to be closed in level 1 cache
- The data is stored in a level 2 cache
- The new session queries the information and can retrieve the content from the level 2 cache
- The data detected by different Mapper is stored in its own cache (map)
The sample
-
Explicitly enable the global cache cacheEnabled, which is enabled by default and is only explicitly visible to developers
<settings>.<setting name="cacheEnabled" value="true"/> </settings> Copy the code
-
If this parameter is enabled in mapper. XML, you can configure the cache policy and time. For details, see the official website
<cache/> <! Readonly ="true" --> Copy the code
-
Test (remember that Sql statements are configured in XML, annotations are not cached!!)
@Test public void testCache02(a){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = mapper.getBlogById("eb7cc29b63164cf0922ded8c4e67a924"); System.out.println(blog); // If the first sqlSession is closed, the second one can still use data sqlSession.close(); System.out.println("= = = = = = = = = = = = = = = = ="); SqlSession sqlSession2 =MybatisUtils.getSqlSession(); BlogMapper mapper2 = sqlSession2.getMapper(BlogMapper.class); Blog blog2 = mapper2.getBlogById("eb7cc29b63164cf0922ded8c4e67a924"); System.out.println(blog2); // Check whether it is the same reference System.out.println(blog==blog2); sqlSession2.close(); } Copy the code
-
The results of
Cache processes
Custom cache
EhCache is a pure Java in-process cache framework, which is fast and concise. It is a widely used open source Java distributed cache.
- Import packages
<! -- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
Copy the code
- The mapper XML
<cache
type="org.mybatis.caches.ehcache"
/>
Copy the code
- Create the ehcache.xml configuration file under resources
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<! -- Disk cache location -->
<diskStore path="java.io.tmpdir/ehcache"/>
<! -- Default cache -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<! -- HelloWorld cache -->
<cache name="HelloWorldCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
Copy the code
- explain
DiskStore: EhCache supports both memory and disk storage. Path: Specifies the disk storage location. DefaultCache: Default cache maxEntriesLocalHeap= "10000" Eternal = "false" timeToIdleSeconds= "120" timeToLiveSeconds= "120" MaxEntriesLocalDisk = "10000000" diskExpiryThreadIntervalSeconds = "120" memoryStoreEvictionPolicy = "LRU cache: If the customized cache configuration does not meet the actual situation, you can customize the cache (including multiple cache nodes) name: MaxElementsInMemory: specifies the maximum number of elements allowed to be stored in memory. 0 indicates unlimited clearOnFlush: Specifies whether to flush the maximum number of elements. Eternal: Sets whether the object in the cache is permanent, if so, the timeout setting is ignored and the object never expires. Depending on the stored data, for example, some static data such as provinces and municipalities can be set to never be outdated. TimeToIdleSeconds: Set the time (in seconds) that the object is allowed to remain idle before it becomes invalid. Used only when eternal=false object is not permanently valid, optional property, default is 0, that is infinite idle time. TimeToLiveSeconds: The time to live (TTL) of cached data, which is the maximum interval between the time an element is built and dies. This is only valid if the element is not permanently resident. A value of 0 means that the element can be paused for an infinite length of time. OverflowToDisk: specifies whether to enable disk caching when the memory is insufficient. MaxEntriesLocalDisk: Ehcache writes objects to disk when the number of objects in memory reaches maxElementsInMemory. MaxElementsOnDisk: indicates the maximum number of caches on a hard disk. DiskSpoolBufferSizeMB: This parameter sets the size of the DiskStore cache. The default is 30MB. Each Cache should have its own buffer. DiskPersistent: Specifies whether to store cached data of disks during VM restart. The default value is false. DiskExpiryThreadIntervalSeconds: disk failure thread running time interval, the default is 120 seconds.Copy the code
Ehcache and Redis
Ehcache is directly cached in the JVM, which is fast and efficient. However, cache sharing is troublesome and cluster distributed application is inconvenient.
Redis is through socket access to the cache service, the efficiency is lower than Ehcache, much faster than the database, processing cluster and distributed cache is convenient, there is a mature solution. Ehcache is used for single applications or applications that require high cache access. If a large system has cache sharing, distributed deployment, and large cache content, redis is recommended.
Ehcache also has a cache sharing scheme, but it is broadcast cache notification update through RMI or Jgroup multicast. Cache sharing is complex and inconvenient to maintain. Simple sharing is ok, but cache recovery, big data caching, is not appropriate.
Custom cache