mybaits (seven) spring integration mybaits

Integration analysis with Spring
http://www.mybatis.org/spring/zh/index.html
Here we take the traditional Spring as an example, because the configuration is more intuitive, the configuration class annotations used in Spring are the same.
 
In the previous course, our programming-based engineering has figured out the MyBatis workflow, core
Heart module and underlying principles. There are three core objects in the programmatic engineering, that is, the native API of MyBatis:
SqlSessionFactory、SqlSession、MapperProxy。
Most of the time, we will not use the MyBatis project alone in the project, but integrate it into Spring so that
Used, but did not see these three objects appear in the code. We directly injected a Mapper interface,
Call its method.
So there are a few key issues, we have to figure out:
1. When was the SqlSessionFactory created?
2. Where did SqlSession go? Why not use it to getMapper?
3. Why does @Autowired inject an interface into a proxy object when it is used? At IOC
What are we injecting into the container? What happened during the injection?
 
Key configuration
Let's take a look at a few things to do when integrating MyBatis into Spring.
In order to make everyone look more intuitive, here we still use the traditional xml configuration to explain to everyone, of course
The effect of using the configuration class @Configuration is also the same. For Spring, it is just the difference of the parsing method.
In addition to the dependency of MyBatis, we also need to introduce the integration of MyBatis and Spring in the pom file
Jar package (note the version! The version of mybatis is compatible with the version of mybatis-spring).
 
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency> 
Then configure SqlSessionFactoryBean in Spring's applicationContext.xml, it
It is used to help us create a session, which also specifies the path of the global configuration file and mapper mapper file.
 
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="dataSource" ref="dataSource"/>
</bean>
Then configure the path of the Mapper interface to be scanned in applicationContext.xml.
There are several ways in Mybatis, the first is to configure a MapperScannerConfigurer.
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gupaoedu.crud.dao"/>
</bean>
The second is to configure a <scan> tag:
<mybatis-spring:scan base-package="com.gupaoedu.crud.dao"/>
Another is to use @MapperScan annotation directly, for example, we add to the startup class of Spring Boot
Previous note:
@SpringBootApplication
@MapperScan("com.gupaoedu.crud.dao")
public class MybaitsApp {
public static void main(String[] args) {
SpringApplication.run(MybaitsApp.class, args);
 
The effect achieved by these three methods is the same.
1. Create a session factory
Spring manages the objects of MyBatis, but it does not replace the core objects of MyBatis. Just
Means: SqlSessionFactory, SqlSession, MapperProxy in MyBatis jar package are all
Will be used. The classes in mybatis-spring.jar just do some packaging or bridge work.
So the first step, let's take a look at how the factory class is created in Spring.
We have configured a SqlSessionFactoryBean in Spring's configuration file, let's take a look at this
Class

 

 

It implements the InitializingBean interface, so it is necessary to implement the afterPropertiesSet () method, this method
It will be called when the bean property value is set.
In addition, it implements the FactoryBean interface, so when it initializes, it actually calls getObject ()
Method, which also calls the afterPropertiesSet () method.
In the afterPropertiesSet () method:
The first step is to check some label attributes, and then call the buildSqlSessionFactory () method.
Then defined a Configuration, called targetConfiguration
Line 426, to determine whether the Configuration object already exists, that is, whether it has been parsed. If already
Once there are objects, just cover the attributes.
433 lines, if Configuration does not exist, but the configLocation attribute is configured, according to
The file path of mybatis-config.xml, build an xmlConfigBuilder object.
436 lines, otherwise, the Configuration object does not exist and the configLocation path does not exist.
Use default properties to build to assign values ​​to configurationProperties.
The following is based on the existing properties in the current factory object, in the targetConfiguration object
Assignment of attributes.
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
This method is an empty method in Java8. If it is not empty, it will call the brackets
Object methods, do assignment processing.
On line 498, if xmlConfigBuilder is not empty, which is the second case above, call
xmlConfigBuilder.parse () to parse the configuration file will eventually return the parsed Configuration object.
On line 507, if no transaction factory is explicitly specified, the default is used
SpringManagedTransactionFactory. The SpringManagedTransaction it creates also has
getConnection () and close () methods.
<property name="transactionFactory" value="" />
On line 520, call xmlMapperBuilder.parse (), this step we have learned before, its work
The purpose is to register the interface and the corresponding MapperProxyFactory to the MapperRegistry.
Finally, calling sqlSessionFactoryBuilder.build () returns a DefaultSqlSessionFactory.
OK, here we have completed the first step in the programmatic case and obtained a factory class based on the configuration file
It is a singleton and will be used later to create SqlSession.
Summary of Spring extension points used: 
 

 

 

2. Create SqlSession
Q1: Can I use DefaultSqlSession directly?
We now have a DefaultSqlSessionFactory, according to the programmatic development process, we receive
It will create an implementation class of SqlSession, but in Spring, we are not directly using
DefaultSqlSession, but to encapsulate it, the implementation class of this SqlSession is
SqlSessionTemplate. This is the same as Spring package other components, such as JdbcTemplate,
RedisTemplate and so on are also the most critical class for Spring and MyBatis integration.
Why not use DefaultSqlSession? It is not thread-safe, pay attention to the annotations on the class:
Note that this class is not Thread-Safe.
The SqlSessionTemplate is thread safe.
* Thread safe, Spring managed, {@code SqlSession} that works with Spring
Recall the life cycle of SqlSession: 
 
Object Life Cycle
SqlSessionFactoryBuiler method local (method)
SqlSessionFactory (single case) application level (application)
SqlSession request and operation (request / method)
Mapper method
 
In programmatic development, we will create a SqlSession at each request, but Spring
There is only one SqlSessionTemplate (default is a singleton), how to protect it when multiple threads are called at the same time
To prove thread safety?
Thinking: Why is SqlSessionTemplate thread-safe?
Thinking: In programming development, is there any way to ensure the thread safety of SqlSession?
There are all methods of DefaultSqlSession in SqlSessionTemplate: selectOne (),
selectList (), insert (), update (), delete (), but it is implemented through a proxy object. This
A proxy object is created by a proxy class in the construction method:
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor()); 
 
All methods will first go to the invoke () method of the internal proxy class SqlSessionInterceptor:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
First, a sqlSession will be created using the factory class, executor type, and exception resolver, and then called
The implementation class of sqlSession is actually calling the method of DefaultSqlSession here
 
Q2: How do I get a SqlSessionTemplate?
We know that in Spring, DefaultSqlSession will be replaced with SqlSessionTemplate, then
Next, look at how to get a SqlSessionTemplate in the DAO layer.
I do n’t know if you have used Hibernate, if you do n’t need to inject, we are in the DAO layer
What is a way to inject a HibernateTemplate?
——Let us implement the DAO layer to inherit HibernateDaoSupport.
The same is true in MyBatis, which provides a SqlSessionDaoSupport, which holds a
SqlSessionTemplate object, and provides a getSqlSession () method, let us get a
SqlSessionTemplate
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
Front and back omitted ...
In other words, if we let the implementation class of the DAO layer inherit SqlSessionDaoSupport, we can get
SqlSessionTemplate, and then encapsulate the method of SqlSessionTemplate.
Of course, in order to reduce duplication of code, we usually do not let our implementation class directly inherit
SqlSessionDaoSupport, but first create a BaseDao inherit SqlSessionDaoSupport. in
BaseDao encapsulates operations on the database, including selectOne (), selectList (), insert (), delete ()
These methods can be directly called by subclasses
public class BaseDao extends SqlSessionDaoSupport {
// Use sqlSessionFactory
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Autowired 
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
public Object selectOne(String statement, Object parameter) {
return getSqlSession().selectOne(statement, parameter);
}
Omitted later ... 
 
Then let our implementation class inherit BaseDao and implement our DAO layer interface, here is our
Mapper interface. The implementation class needs to be annotated with @Repository.
In the method of the implementation class, we can directly call the selectOne () method encapsulated by the parent class (BaseDao),
Then it will eventually call the selectOne () method of sqlSessionTemplate.
@Repository
public class EmployeeDaoImpl extends BaseDao implements EmployeeMapper {
@Override
public Employee selectByPrimaryKey(Integer empId) {
Employee emp = (Employee)
this.selectOne("com.gupaoedu.crud.dao.EmployeeMapper.selectByPrimaryKey",empId);
return emp;
}
Omitted later ...
Then in the place where you need to use, such as the Service layer, inject our implementation class and call the method of the implementation class
okay. Here we directly inject in the unit test class:
@Autowired
EmployeeDaoImpl employeeDao;
@Test
public void EmployeeDaoSupportTest() {
System.out.println(employeeDao.selectByPrimaryKey(1));
}
Will eventually call the method of DefaultSqlSession. 
 
Q3: Is there a better way to get SqlSessionTemplate? 
 
There is a problem with this: every interface of our DAO layer (the Mapper interface also belongs to), if you want to
Get a SqlSessionTemplate, to operate the database, you must create an implementation class, plus
@ Repository's annotation, inheriting BaseDao, this workload is not small.
Another one, we went directly to call the selectOne () method, or the Statement ID was hard-coded,
MapperProxy is useless here.
How can we inject Mapper elsewhere without creating any implementation class
Use, and can get SqlSessionTemplate to operate the database?
This is indeed our usage in Spring. Then we need to figure out, we just injected one
How does the interface get the SqlSessionTemplate when the object is instantiated? When we call the method
Wait, is it MapperProxy?
 
3. Scanning registration of the interface
In the Service layer, you can use the Mapper interface automatically injected by @Autowired, which needs to be saved in
BeanFactory (such as XmlWebApplicationContext). In other words, the interface must be in Spring
It was scanned at startup and registered.
1. When was it scanned?
2. When registering, what was registered? This determines what actual object we get.
Recall that we configured a in applicationContext.xml
MapperScannerConfigurer.
MapperScannerConfigurer implements the BeanDefinitionRegistryPostProcessor interface,
BeanDefinitionRegistryPostProcessor is a subclass of BeanFactoryPostProcessor, you can
Modify, add, or delete definitions of certain beans through coding

 

 

We only need to rewrite the postProcessBeanDefinitionRegistry () method to manipulate the Bean in it
That's it.
In this method:
The scanner.scan () method is in ClassPathBeanDefinitionScanner, and its subclasses
ClassPathMapperScanner overrides the doScan () method and is called in doScan ()
processBeanDefinitions:
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) +
"' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
It first calls doScan () of the parent class to scan all interfaces.
 
processBeanDefinitions method, when registering beanDefinitions, BeanClass
Was changed to MapperFactoryBean (note the gray note).
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder: beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + beanClassName + "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); //
issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
problem:
Why should BeanClass be changed to MapperFactoryBean, what does this class do?
MapperFactoryBean inherits SqlSessionDaoSupport, you can get
SqlSessionTemplate。
 
4. Interface injection
When we use Mapper, we only need to use @Autowired in the class annotated with Service
Just inject the Mapper interface.
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
public List<Employee> getAll() {
return employeeMapper.selectByMap(null);
}
 
Spring needs to instantiate EmployeeService when it starts.
EmployeeService relies on the EmployeeMapper interface (which is a property of EmployeeService
Sex).
Spring will get its BeanDefination from BeanFactory according to the name of Mapper, and then
Get BeanClass in BeanDefination, the BeanClass corresponding to EmployeeMapper is
MapperFactoryBean (analyzed in the previous step).
The next step is to create MapperFactoryBean, because the FactoryBean interface is implemented, the same is called
Use the getObject () method.
// MapperFactoryBean.java
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
Because MapperFactoryBean inherits SqlSessionDaoSupport, so this
getSqlSession () is to call the method of the parent class and return SqlSessionTemplate.
// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
The second step is the getMapper () method of SqlSessionTemplate, which has two methods:
// SqlSessionTemplate.java
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
The first step: the getConfiguration () method of SqlSessionTemplate:
// SqlSessionTemplate.java
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
 
Enter the method, through DefaultSqlSessionFactory, return all configuration Configuration:
// DefaultSqlSessionFactory.java
public Configuration getConfiguration() {
return configuration;
}
Step 2: The getMapper () method of Configuration:
// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
We are very familiar with this step, just like using getMapper programmatically, through the factory class
MapperProxyFactory obtains a MapperProxy proxy object.
In other words, the interface we injected into the Service layer is actually a MapperProxy object.
So the last method to call the Mapper interface is also to execute the MapperProxy's invoke () method.
The process is exactly the same as in a programming project.
 

 

Thinking: How is @MapperScan annotation parsed?
 

 

 

Guess you like

Origin www.cnblogs.com/flgb/p/12688416.html