Integration of Spring and mybatis and weaving of transactions
Integrate Mybatis
Create dependency
We need junit, mysql driver, mybatis, spring webmvc, spring JDBC, aspectjweaver
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.2</version> </dependency> </dependencies>
Review Mybatis
Set the static resource filtering of maven (otherwise, mapper.xml cannot be found will appear later)
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
-
Create a new user entity class (Note: it corresponds to the field of the user table in the database)
package com.lwh.pojo; public class User { private int id; private String name; private String pwd; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
-
Create a new UserMapper interface
package com.lwh.mapper; import com.lwh.pojo.User; import java.util.List; public interface UserMapper { public List<User> selectUser(); }
-
Write usermapper xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lwh.mapper.UserMapper"> <select id="selectUser" resultType="User"> select * from user; </select> </mapper>
-
Write the configuration file mybatis config XML and bind usermapper XML (remember)
<?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> <!--Set alias--> <typeAliases> <package name="com.lwh.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--every last xml You have to register--> <mappers> <mapper resource="com/lwh/mapper/UserMapper.xml"/> </mappers> </configuration>
-
Writing test classes
@org.junit.Test public void t1() throws IOException { String resources="mybatis-config.xml"; InputStream asStream = Resources.getResourceAsStream(resources); SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(asStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user : users) { System.out.println(user); } /**Output: * User{id=1, name='a', pwd='12345'} * User{id=2, name='b', pwd='123455'} */ sqlSession.close(); }
Gradually integrate Spring and mybatis
The purpose of spring is to hand over the creation and management of objects to the framework. We can just go in and get them when we need to use them. Therefore, the new keyword that appears many times in mybatis is not allowed. Therefore, all the operations of key objects of mybatis are left to spring, that is, bean configuration
Mode 1
Write the data source configuration in the Spring configuration file
<!--Configure data sources, using spring Data source replacement for mybatis The configuration used is Spring Provided by the framework JDBC,That's why we imported it before spring-jdbc Reasons for--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
Give the sqlSessionFactory of MyBatis to Spring for hosting, that is, create a bean
<!--establish SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--binding mybatis Configuration file for--> <!--This sentence stands for this sqlSessionFactory Already and spring With its own configuration file--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!--This sentence stands for binding mapper,such mybatis No binding is required in the configuration file mapper Yes--> <property name="mapperLocations" value="classpath:com/lwh/mapper/*.xml"/> </bean>
Give the sqlSessionTemplate of MyBatis to Spring for hosting and create a bean
<!--establish sqlSessionTemplate That is, we are mybatis Used in SqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--Only through the constructor assignment, you can see the source code SqlSessionTemplate There is a parameter constructor in and needs to be passed in SqlSessionFactory--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
The implementation class usermapperinpl needs to be added to the interface to inherit the interface UserMapper
package com.lwh.mapper; import com.lwh.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; //Give spring an injected interface set method public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> selectUser() { UserMapper mapper=sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }
Then send this class to Spring for hosting
<!--Spring Managed implementation class--> <bean id="userMapper" class="com.lwh.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
test
@org.junit.Test public void t1() throws IOException { ApplicationContext context = new ClassPathXmlApplicationContext("SpringApplication.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); List<User> users = userMapper.selectUser(); for (User user : users) { System.out.println(user); } /** * User{id=1, name='a', pwd='12345'} * User{id=2, name='b', pwd='123455'} */ }
Mode 2 (simplified on the basis of mode 1)
The first two parts are the same. In the third step, we can write the implementation class directly without giving sqlsession to Spring for hosting, that is, without configuring the bean of sqlsession
UserMapperImpl2
package com.lwh.mapper; import com.lwh.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { /** * SqlSessionDaoSupport This parent class has helped us to obtain sqlsession * However, the parent class SqlSessionDaoSupport still needs to obtain the object SqlSessionFactory, so the creation of SqlSessionFactory is essential * @return */ @Override public List<User> selectUser() { return getSqlSession().getMapper(UserMapper.class).selectUser(); } }
Leave the implementation class to Spring for hosting
<!--Spring Implementation class of hosting mode 2--> <bean id="userMapper2" class="com.lwh.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
test
@org.junit.Test public void t2(){ ApplicationContext context = new ClassPathXmlApplicationContext("SpringApplication.xml"); UserMapper userMapper2 = context.getBean("userMapper2", UserMapper.class); for (User user : userMapper2.selectUser()) { System.out.println(user); } /** * Output: * User{id=1, name='a', pwd='12345'} * User{id=2, name='b', pwd='123455'} */ }
Declarative transaction
Transaction:
- Either all succeed or all fail
- Transaction is very important in project development, which involves the consistency of data
- Ensure integrity and consistency
Transaction ACID principle
- Atomicity
- uniformity
- Isolation
- Multiple services may operate the same resource to prevent data corruption
- persistence
- Once the transaction is committed, it will be written to the physical file to persist the data
for example
Writing pojo classes
package com.lwh.pojo; public class User { private int id; private String name; private String pwd; public User(){ } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
Import maven dependencies
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.2</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
Write the configuration file for the integration of Spring and mybatis, Spring Dao xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <!--Configure data sources, using spring Data source replacement for mybatis The configuration used is Spring Provided by the framework JDBC,That's why before we import spring-jdbc Reasons for--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!--establish SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--binding mybatis Configuration file for--> <!--This sentence stands for this sqlSessionFactory Already and spring With its own configuration file--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!--This sentence stands for binding mapper,such mybatis No binding is required in the configuration file mapper Yes--> <property name="mapperLocations" value="classpath:com/lwh/mapper/*.xml"/> </bean> <!--establish sqlSessionTemplate That is, we are mybatis Used in SqlSession,This step can be omitted in the second implementation method!!!--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--Only through the constructor assignment, you can see the source code SqlSessionTemplate There is a parameter constructor in and needs to be passed in SqlSessionFactory--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> </beans>
Writing spring application bean configuration files
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <import resource="Spring-dao.xml"/> <bean id="userMapperImpl" class="com.lwh.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
Write UserMapper interface
package com.lwh.mapper; import com.lwh.pojo.User; import java.util.List; public interface UserMapper { //increase public void add(User user); //delete public void delete(int id); //modify public void update(User user); //check public List<User> query(); }
Write usermapper xml
be careful! There is an error, "deletes"! In order to test, we need to know what happens when the transaction is not configured
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lwh.mapper.UserMapper"> <select id="query" resultType="User"> select * from user; </select> <insert id="add" parameterType="User"> insert into user(id,name,pwd) values (#{id},#{name},#{pwd}) </insert> <delete id="delete" parameterType="int"> deletes from user where id=#{id} </delete> </mapper>
Write the mybatis config configuration file
<?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> <typeAliases> <package name="com.lwh.pojo"/> </typeAliases> </configuration>
Write UserMapperImpl
package com.lwh.mapper; import com.lwh.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; //Give spring an injected interface set method public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public void add(User user) { UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.add(user); mapper.delete(3); } @Override public void delete(int id) { } @Override public void update(User user) { } @Override public List<User> query() { return null; } }
Writing test classes
There were two people with id 1 and 2 in the database. I want to insert a person with id 3 into it, and then delete it
import com.lwh.mapper.UserMapper; import com.lwh.mapper.UserMapperImpl; import com.lwh.pojo.User; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org.junit.Test public void t1(){ ApplicationContext context = new ClassPathXmlApplicationContext("SpringApplication.xml"); UserMapper userMapperImpl = context.getBean("userMapperImpl", UserMapper.class); userMapperImpl.add(new User(3,"Li Hua","12345")); } }
Result analysis
Sure enough, an error was reported
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'deletes from user where id=3' at line 1
Because my delete statement is written incorrectly, this error is reported. We hope that after this method is executed, if there is an error, other operations on the database also need to be invalidated. It should be consistent and not semi correct. This is obviously unreasonable. We need to declare transactions in Spring
We made changes to the code
Modify spring Dao XML, add the following code
<!--combination AOP Implement transaction weaving--> <!--Configure notifications for transactions--> <tx:advice id="txAdvice" transaction-manager="transationManager"> <tx:attributes> <!--propagation Indicates the propagation of a transaction--> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" propagation="REQUIRED"/> <!--Generally, you only need to configure the following statements. The example code indicates that a method can be configured--> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.lwh.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
You can test directly. After the deletion error is found, no record with id 3 will appear! Transaction weaving succeeded!
I wish you a happy study! Common progress~