A simple example of MyBatis

Posted by JJ123 on Fri, 17 Dec 2021 08:41:03 +0100

Introduction to MyBatis

MyBatis is an excellent persistence layer framework that supports customized SQL, stored procedures, and advanced mapping.
MyBatis avoids almost all JDBC code and manually setting parameters and getting result sets.
MyBatis can use simple XML or annotations for configuration and original mapping, and map the interface and Java POJO(Plain Old Java Objects) into records in the database

MyBatis history
• ibatis was originally an open source project of Apache. In June 2010, the project was migrated from Apache Software Foundation to Google Code. With the development team switching to Google Code, ibatis 3 X was officially renamed MyBatis, and the code was migrated to GitHub in November 2013 (see the download address below).
• iBatis comes from the combination of "internet" and "abatis". It is a persistence layer framework based on Java. The persistence layer framework provided by iBatis includes SQL Maps and Data Access Objects (DAO)

Why use MyBatis?
• MyBatis is a semi automated persistence layer framework.
• JDBC
– SQL is sandwiched in Java code blocks, and high coupling leads to hard coding internal injury
– it is difficult to maintain and the sql changes in the actual development requirements. Frequent modifications are common
• Hibernate and JPA
– long and complex SQL is not easy to handle for Hibernate
– SQL automatically produced internally is not easy to perform special optimization.
– the fully automated framework based on full mapping makes it difficult to partially map POJO s with a large number of fields. This leads to the degradation of database performance.
• for developers, the core sql still needs to be optimized by themselves
• sql and java coding are separated, and the functional boundary is clear. One focuses on business and the other on data.

MyBatis official website

Official website: https://github.com/mybatis/mybatis-3/

MyBatis usage steps

1. Create a database table

2. Create the JavaBean corresponding to the database

package com.mybatis.bean;

public class Employee {
	
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email="
				+ email + ", gender=" + gender + "]";
	}
}

3. Create the global configuration file mybatis config xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
				<property name="username" value="root" />
				<property name="password" value="123456" />
			</dataSource>
		</environment>
	</environments>
</configuration>

4. Add the SQL mapping file employeemapper xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.dao.EmployeeMapper">
<!-- 
namespace:Namespace;Specifies the full class name of the interface
id: Unique identification
resultType: return type
#{id}: get the id value from the passed parameter
 -->
</mapper>

5. Register the SQL mapping file to the global configuration file

<configuration>
	<environments default="development">
	</environments>
	<!-- Will we write it sql Mapping file( EmployeeMapper.xml)Be sure to register with the global configuration file( mybatis-config.xml)in -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>

MyBatis running

1. Create SqlSessionFactory according to the global configuration file

public class MyBatisTest {
	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}
}

2. Use SqlSessionFactory to obtain sqlSession object

One SqlSession Object represents a session with the database.
public class MyBatisTest {
	@Test
	public void test() throws IOException {

		// 2. Get the sqlSession instance and directly execute the mapped sql statements
		// Unique identifier of sql: statement Unique identifier matching the statement to use
		// parameter A parameter object to pass to the statement
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// A SqlSession object represents a session with the database
		SqlSession openSession = sqlSessionFactory.openSession();
	}
}

MyBatis example: new interface

1. Create a Dao interface - employeemapper java

import com.mybatis.bean.Employee;
public interface EmployeeMapper {
	public Employee getEmpById(Integer id);
}

2. The Dao interface is registered to the SQL mapping file employeemapper xml

<mapper namespace="com.mybatis.dao.EmployeeMapper">
	<!--  public Employee getEmpById(Integer id);  -->
	<select id="getEmpById" resultType="com.mybatis.bean.Employee">
		select id,last_name lastName,email,gender from tbl_employee where id = #{id}
	</select>
</mapper>

3. Use SqlSession object for database operation

Method 1: use the selectOne method

public class MyBatisTest {
	@Test
	public void test() throws IOException {
		// 1. Get sqlSessionFactory object
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2. Get sqlSession object
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			Employee employee = openSession.selectOne(
					"com.mybatis.EmployeeMapper.getEmpById", 1);
		} finally {
			openSession.close();
		}
	}
}

Method 2: get mapper

public class MyBatisTest {
	@Test
	public void test() throws IOException {
		// 1. Get sqlSessionFactory object
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2. Get sqlSession object
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			// 3. Gets the implementation class object of the interface
			//A proxy object will be automatically created for the interface, and the proxy object will execute the addition, deletion, modification and query methods
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee employee = mapper.getEmpById(1);
		} finally {
			openSession.close();
		}
	}
}

MyBatis - global configuration file MyBatis config xml

The MyBatis configuration file contains settings and properties information that deeply affect MyBatis behavior.

The top-level structure of the document is as follows:

MyBatis - mapping file employeemapper xml

The real power of MyBatis lies in its statement mapping, which is its magic. Because of its extraordinary power, the XML file of the mapper is relatively simple. If you compare it with JDBC code with the same function, you will immediately find that nearly 95% of the code is saved. MyBatis is committed to reducing the use cost and enabling users to focus more on SQL code.

SQL The mapping file has only a few top-level elements (listed in the order in which they should be defined):
	cache – Cache configuration for this namespace.
	cache-ref – Cache configuration that references other namespaces.
	resultMap – Describing how to load objects from the database result set is the most complex and powerful element.
	parameterMap – Old style parameter mapping. This element has been discarded and may be removed in the future! Please use inline parameter mapping. This element is not described in the document.
	sql – A repeatable sentence block that can be referenced by other statements.
	insert – Map insert statement.
	update – Map update statement.
	delete – Map delete statement.
	select – Map query statements.

MyBatis - dynamic SQL

Dynamic SQL is one of the powerful features of MyBatis.
MyBatis dynamic SQL Element:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

MyBatis - caching mechanism

MyBatis includes a very powerful query caching feature that can be easily configured and customized. Caching can greatly improve query efficiency.

MyBatis Two level cache is defined by default in the system: L1 cache and L2 cache.
	– 1,By default, there is only one level cache(SqlSession Level cache, also known as local cache)Open.
	– 2,L2 cache needs to be manually enabled and configured. It is based on namespace Level cache.
	– 3,To improve scalability. MyBatis Cache interface defined Cache. Can be achieved by Cache Interface to customize L2 cache.

L1 cache SqlSession

Local cache, that is, local cache. The scope is sqlSession by default.
When a Session is flushed or close d, all caches in the Session will be emptied.
The local cache cannot be closed, but clearCache() can be called to empty the local cache or change the scope of the cache

In mybatis3 After 1, you can configure the scope of the local cache In mybatis Configuration in XML:

L1 cache demo & invalidation

  • During the same session, any queried data will be saved in a Map of the current SqlSession
    • key:hashCode + SqlId of query + sql query statement written + parameters
  • Four situations of L1 cache invalidation
    – 1. Different sqlsessions correspond to different L1 caches
    – 2. The same SqlSession but different query conditions
    – 3. Any addition, deletion or modification is performed during two queries of the same SqlSession
    – 4. The cache is manually emptied during two queries of the same SqlSession

sqlSession details: https://mybatis.org/mybatis-3/zh/java-api.html#sqlSessions

Second level cache

Second level cache, global scope cache.
L2 cache is not enabled by default and needs to be configured manually
MyBatis provides the interface and implementation of L2 cache. The cache implementation requires POJO to implement the Serializable interface
L2 cache will not take effect until SqlSession is closed or committed

Use steps:
	– 1,Enable L2 cache in global configuration file
		<setting name="cacheEnabled" value="true"/>
	– 2,Used at mapping files that require L2 caching cache Configure cache 
		<cache />
	– 3,be careful:POJO Need to implement Serializable Interface

Cache related properties:

  • Occurrence = "FIFO": cache recycling policy:
    • LRU – least recently used: removes objects that have not been used for the longest time.
    • FIFO – first in first out: remove objects in the order they enter the cache.
    • SOFT – SOFT reference: removes objects based on garbage collector status and SOFT reference rules.
    • WEAK – WEAK references: more actively remove objects based on garbage collector status and WEAK reference rules.
    • The default is LRU.
  • Flush interval: refresh interval, in milliseconds
    • The default is not set, that is, there is no refresh interval. The cache is only refreshed when the statement is called
  • size: number of references, positive integer
    • Represents the maximum number of objects that can be stored in the cache. Too large can easily lead to memory overflow
  • readOnly: read only, true/false
    • true: read only cache; The same instance of the cache object is returned to all callers. So these objects
    • False: read / write cache; A copy of the cached object is returned (by serialization). This will be slower, but safe, so the default is false.

Cache settings:

• 1. cacheEnable of global setting:
– configure the switch for L2 cache. The L1 cache is always on.
• 2. useCache attribute of select tag:
– configure whether the select uses L2 cache. L1 cache is always used
• 3. flushCache attribute of sql tag:
– add, delete or change the default flushCache=true. After the sql is executed, the L1 and L2 caches will be emptied at the same time.
The query defaults to flushCache=false.
• 4,sqlSession.clearCache():
– only used to clear the L1 cache.
• 5. When a C/U/D operation is performed in a scope (L1 cache Session / L2 cache Namespaces), by default, the caches in all select in the scope will be clear ed.

Third party cache consolidation

EhCache is a pure Java in-process caching framework, which is fast and lean. It is the default CacheProvider in Hibernate.

MyBatis defines a Cache interface to facilitate our custom extension.

step:
	– 1,Import ehcache Package, consolidation package and log package 
		ehcache-core-2.6.8.jar,mybatis-ehcache-1.0.3.jar slf4j-api-1.6.1.jar,slf4j-log4j12-1.6.2.jar
	– 2,to write ehcache.xml configuration file 
	– 3,to configure cache label
		<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

Reference cache:If you want to share the same cache configuration and instances in the namespace. have access to cache-ref Element to reference another cache.
<cache-ref namespace="com.mybatis.example.CustomerMapper" />

MyBatis - Spring integration

1. View the adaptation packages used by different MyBatis versions to integrate Spring: http://www.mybatis.org/spring/
2. Download the consolidated adapter package: https://github.com/mybatis/spring/releases

3. Official integration example - jpetstore: https://github.com/mybatis/jpetstore-6

Consolidate critical configurations:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- appoint mybatis Global profile location -->
	<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> 
	<!--specify data source -->
	<property name="dataSource" ref="dataSource"></property> 
	<!--mapperLocations:All sql Location of the mapping file -->
	<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property> 
	<!--typeAliasesPackage:Batch alias processing-->
	<property name="typeAliasesPackage" value="com.atguigu.bean"></property>
</bean>
	
<!--Automatically scan all mapper Implement and add to ioc In container -->
<bean id="configure" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!– basePackage:Specify all under the package mapper Interface to automatically scan and join ioc In container -->
	<property name="basePackage" value="com.atguigu.dao"></property> 
</bean>

MyBatis - Reverse Engineering

MyBatisGenerator:
• MBG, for short, is a code generator specially customized for MyBatis framework users. It can quickly generate corresponding mapping files, interfaces and bean classes according to the table. It supports basic addition, deletion, modification and query, as well as QBC style conditional query. However, the definitions of complex sql such as table joins and stored procedures need to be written manually

Official document address: http://www.mybatis.org/generator/
Official project address: https://github.com/mybatis/generator/releases

MBG Use steps:
– 1,to write MBG Configuration file for(Several important configurations) 
	1)jdbcConnection Configure database connection information
	2)javaModelGenerator to configure javaBean Generation strategy for 
	3)sqlMapGenerator to configure sql Mapping file generation policy 
	4)javaClientGenerator to configure Mapper Generation strategy of interface 
	5)table Configure the data table to reverse parse: tableName:Table name domainObjectName:Corresponding javaBean name
– 2,Run the code generator to generate code

be careful: Context label
	targetRuntime="MyBatis3"Add, delete, modify and query with conditions can be generated targetRuntime="MyBatis3Simple"Basic addition, deletion, modification and query can be generated
	If it is generated again, it is recommended to delete the previously generated data to avoid xml Problems with appending content back.

MBG profile:

<generatorConfiguration>
	<context id="DB2Tables" targetRuntime="MyBatis3">
		//Database connection information configuration
		<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/bookstore0629" userId="root" password="123456">
		</jdbcConnection>
		//Generation strategy of javaBean
		<javaModelGenerator targetPackage="com.atguigu.bean" targetProject=".\src">
		<property name="enableSubPackages" value="true" />
		<property name="trimStrings" value="true" />
		</javaModelGenerator>
		//Generation strategy of mapping file
		<sqlMapGenerator targetPackage="mybatis.mapper" targetProject=".\conf">
		<property name="enableSubPackages" value="true" />
		</sqlMapGenerator>
		//Generation strategy of dao interface java file
		<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.dao"
		targetProject=".\src">
		<property name="enableSubPackages" value="true" /> </javaClientGenerator>
		//Mapping between data table and javaBean
		<table tableName="books" domainObjectName="Book"></table>
	</context> 
</generatorConfiguration>

Generator code:

public static void main(String[] args) throws Exception {
	List<String> warnings = new ArrayList<String>();
	boolean overwrite = true;
	File configFile = new File("mbg.xml");
	ConfigurationParser cp = new ConfigurationParser(warnings); 
	Configuration config = cp.parseConfiguration(configFile); 
	DefaultShellCallback callback = new DefaultShellCallback(overwrite); 	
	MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
	myBatisGenerator.generate(null); 
}

Test query: QBC style query with conditions

@Test
public void test01(){
	SqlSession openSession = build.openSession();
	DeptMapper mapper = openSession.getMapper(DeptMapper.class); 
	DeptExample example = new DeptExample(); //All conditions are encapsulated in example
	Criteria criteria = example.createCriteria();
	//select id, deptName, locAdd from tbl_dept WHERE
	//( deptName like ? and id > ? )
	criteria.andDeptnameLike("%Department%");
	criteria.andIdGreaterThan(2);
	List<Dept> list = mapper.selectByExample(example);
	for (Dept dept : list) {
		System.out.println(dept); 
	}
}

MyBatis - how it works

MyBatis - plug in development

MyBatis has plug-ins to intervene in the creation of the four objects. The plug-in can use the dynamic proxy mechanism to wrap the target object layer by layer to achieve the effect of interception before the target object executes the target method.

MyBatis allows intercepting calls at some point during the execution of mapped statements.
By default, MyBatis allows plug-ins to intercept method calls, including:
• Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
• ParameterHandler (getParameterObject, setParameters)
• ResultSetHandler (handleResultSets, handleOutputParameters) • StatementHandler (prepare, parameterize, batch, update, query)

Plug in development steps

1. Write a plug-in to implement the Interceptor interface, and use the @ Intercepts annotation to complete the plug-in signature

2. Register the plug-in in the global configuration file

Plug in principle

1) . according to the plug-in annotation declaration, call the plug-in plugin method according to the plug-in configuration order to generate the dynamic proxy of the intercepted object
2) . multiple plug-ins generate proxy objects of the target object in turn, wrap them layer by layer, and wrap those declared first; Form an agent chain
3) . when executing the target method, execute the intercept method of the plug-in from outside to inside.

4) In the case of multiple plug-ins, we often need to separate the target object from a plug-in. You can use the SystemMetaObject class provided by MyBatis to obtain the h and target attribute values of the last layer

Interceptor interface

Intercept:Intercept target method execution
plugin:To generate dynamic proxy objects, you can use MyBatis Provided Plugin Class wrap method
setProperties:Properties set when injecting plug-in configuration


Common code: separate the real proxy object from the proxy chain

//1. Detach the proxy object. Since multiple proxies will be formed, the final proxied object needs to be separated through a while loop to facilitate the extraction of information
MetaObject metaObject = SystemMetaObject.forObject(target);
while (metaObject.hasGetter("h")) { 
	Object h = metaObject.getValue("h");
	metaObject = SystemMetaObject.forObject(h); 
}
//2. Gets the real proxy object contained in the proxy object
Object obj = metaObject.getValue("target"); 
//3. Get the MetaObject of the proxy object to facilitate information extraction 
MetaObject forObject = SystemMetaObject.forObject(obj);

Extension: MyBatis utility scenario

1. Paging by PageHelper plug-in

PageHelper is a very convenient third-party paging plug-in in MyBatis.
Official documents: https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md

Use steps:
1. Import the related packages pagehelper-x.x.x.jar and jsqlparser-0.9 5.jar.
2. Configure the paging plug-in in the MyBatis global configuration file.

3. Use the methods provided by PageHelper to page
4. You can use a more powerful PageInfo to encapsulate the returned results

2. Batch operation

  • The default openSession() method has no parameters. It creates a transaction with the following characteristics - it starts a transaction (that is, it does not commit automatically)
    – the connection object is obtained from the data source instance configured by the active environment.
    – the transaction isolation level will use the default settings of the driver or data source.
    – preprocessing statements are not reused and updates are not processed in batches.
  • Parameter of ExecutorType type of openSession method, enumeration type:
    – ExecutorType.SIMPLE: this actuator type does nothing special (this is the default assembly). It creates a new preprocessing statement for the execution of each statement.
    – ExecutorType.REUSE: This executor type reuses preprocessing statements.
    – ExecutorType.BATCH: This executor will execute all update statements in batch.

Batch operations are performed by using the batch executor provided by MyBatis, and its bottom layer is performed by saving sql through jdbc. We can ask him to save enough and send it to the database once.

public void test01() {
	SqlSession openSession = build.openSession(ExecutorType.BATCH); 
	UserDao mapper = openSession.getMapper(UserDao.class);
	long start = System.currentTimeMillis();
	for (int i = 0; i < 1000000; i++) {
		String name = UUID.randomUUID().toString().substring(0, 5); 
		mapper.addUser(new User(null, name, 13));
	}
	openSession.commit(); openSession.close();
	long end = System.currentTimeMillis(); 
	System.out.println("Time consuming:"+(end-start));
} 

In the integration with Spring, we recommend configuring an additional sqlSession that can be used to perform batch operations:

When batch operations are needed, we can inject the configured batch SqlSession. Get the mapper mapper through him for operation.

be careful:
1. Batch operation is in session The number of sql statements to be sent after commit()
Executed according to the library
2. If we want it to be executed in advance to facilitate subsequent possible query operations to obtain data, we can use sqlsession The flushstatements () method to flush it directly to the database for execution.

3. Stored procedure

In actual development, we usually write some stored procedures, and MyBatis also supports the call of stored procedures.

One of the simplest stored procedures
	delimiter $$
	create procedure test()
	begin
		select 'hello';
	end $$ 
	delimiter ;

 Call of stored procedure 
 	1,select In label statementType="CALLABLE" 
 	2,Call syntax in label body:
		{call procedure_name(#{param1_info},#{param2_info})}

Stored procedure - cursor handling:
MyBatis provides a JdbcType=CURSOR support for stored procedure cursors, which can intelligently map the data read by the cursor to the result set we declare.

Call instance:

public class PageEmp { 
	private int start; 
	private int end; 
	private int count; 
	private List<Emp> emps;
}
<environment id="oracle_dev"> 
	<transactionManager type="JDBC" /> 
	<dataSource type="POOLED">
		<property name="driver" value="${orcl.driver}" /> 
		<property name="url" value="${orcl.url}" /> 
		<property name="username" value="${orcl.username}" /> 
		<property name="password" value="${orcl.password}" />
    </dataSource>
</environment>
 <databaseIdProvider type="DB_VENDOR"> 
	 <property name="MySQL" value="mysql"/> 
	 <property name="Oracle" value="oracle"/>
</databaseIdProvider>

4. typeHandler handles enumeration

We can customize the parameter encapsulation policy when setting parameters or fetching the result set in the form of custom TypeHandler.

step:
– 1,realization TypeHandler Interface or inheritance BaseTypeHandler 
– 2,use@MappedTypes Define the process java type
	use@MappedJdbcTypes definition jdbcType type
– 3,When customizing the result set label or parameter processing, declare to use custom 
	TypeHandler Process
	Or in global configuration TypeHandler To process javaType

Test example

An enumeration class representing Department status

1. Test global configuration EnumOrdinalTypeHandler

2. Test global configuration EnumTypeHandler

3. Test parameter location setting custom TypeHandler

4. Custom TypeHandler

Topics: Java Mybatis SQL