Spring框架除了提供了IOC、DI和AOP等的核心功能,还支持数据访问和集成功能。可以将mybatis框架整合到Spring框架中来实现对数据库的操作。
为了让两个框架正常工作,需要导入他们各自的依赖包。此外为了让mybatis框架能正常整合到Spring中还需要导入一个mybatis-spring
依赖包。
之前单独使用mybatis的时候,需要编写一个mybatis.xml文件对mybatis进行配置。而现在由于整合到spring框架中了,所以对mybatis的配置也可以在applicationContext.xml文件中完成。
SqlSessionFactoryBuilder
来获取sqlSessionFactory
的,现在由于spring的ioc作用,可以通过配置bean来获取该对象。
<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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bear.sxt.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
bean>
<bean id="userServiceImpl" class="com.bear.sxt.service.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
bean>
beans>
通过编写实体类完成类与数据库表的关系映射,例如定义一个user表,里面有id号、用户名和密码。
public class User {
private int id;
private String username;
private String password;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
编写相应的mapper.xml和接口类,由于2.2中的配置,框架会自动生成一个相应实现类。
<mapper namespace="com.bear.sxt.mapper.UserMapper">
<select id="selectByUser" resultType="com.bear.sxt.pojo.User" parameterType="com.bear.sxt.pojo.User">
select *
from user
where username = #{username} and password = #{password};
select>
mapper>
public interface UserMapper {
User selectByUser(User user);
}
如我们需要在UserServiceImpl中调用UserMapper中的sql语句,需要定义一个UserMapper类型(就是之前定义的那个接口类)的成员变量,并编写get/set方法。然后就可以直接使用UserMapper实现类的方法了。
public class UserServiceImpl implements UserService {
UserMapper userMapper;
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public User login(User user) {
return userMapper.selectByUser(user);
}
}
<bean id="userServiceImpl" class="com.bear.sxt.service.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
bean>
上述的select操作没有涉及事务提交、session关闭、错误回滚等的操作,对于读操作来说这样是不会看出有什么错误的,但对于增、删、改操作来说,如果没有提交事务,则不会产生真正的作用的。Spirng框架有AOP功能所以可以使用AOP来插入上述的事务操作。
在2的配置基础上加上txManager
、txAdivce
和aop
配置。由于是声明式事务由spring写好,所以我们无需理会具体的实现,按照下面的配置配置即可给相应的方法使用上事务。
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="select*" propagation="REQUIRED"/>
<tx:method name="insert*"/>
<tx:method name="*" read-only="true"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="myPointCut" expression="execution(* com.bear.sxt.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointCut"/>
aop:config>
beans>
<mapper namespace="com.bear.sxt.mapper.UserMapper">
<select id="selectByUser" resultType="com.bear.sxt.pojo.User" parameterType="com.bear.sxt.pojo.User">
select *
from user
where username = #{username} and password = #{password};
select>
<insert id="insertUser" parameterType="com.bear.sxt.pojo.User">
insert into user (id, username, password) values (default, #{username}, #{password});
insert>
mapper>
public class UserServiceImpl implements UserService {
UserMapper userMapper;
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public User selectByUser(User user) {
return userMapper.selectByUser(user);
}
@Override
public int insertUser(User user) {
return userMapper.insertUser(user);
}
}
当一个具有事务控制的方法被另一个有事务控制的方法调用时,如何对事务进行管理。可以通过tx:method
标签中的propagation
属性进行设置。如下面的代码中在插入用户前需要检测数据库中是否已经存在了这个用户,即具有事务控制的方法insertUser
调用了另一个具有事务控制的方法selectByUser
。这两个方法的事务如何管理(新建事务、在同一个事务中执行、报异常…),这时就需要设置事务的传播行为了。
@Override
public User selectByUser(User user) {
return userMapper.selectByUser(user);
}
@Override
public int insertUser(User user) {
User temp = userMapper.selectByUser(user);
if (null == temp) {
return userMapper.insertUser(user);
} else {
return 0;
}
}
propagation属性有如下可选值:
用于在多线程或并发访问下保证访问到的数据具有有效性。
并发引起的问题
数据库隔离级别
isolation属性有以下可选值
如,在insert*方法中设置出现java.lang.Exception异常的时候事务回滚。
<tx:method name="insert*" rollback-for="java.lang.Exception"/>
当检测到同名同密码的user时抛出Exception异常,由于设置了异常回滚所以不会插入test用户信息,如果没有设置异常回滚,则会插入test用户信息
public int insertUser(User user) throws Exception{
User temp = userMapper.selectByUser(user);
if (null == temp) {
return userMapper.insertUser(user);
} else {
/*测试异常回滚*/
User test = new User();
test.setUsername("test");
test.setPassword("password");
userMapper.insertUser(test);
throw new Exception("用户已存在");
}
}