1. 导入依赖
- 第一步新建Maven项目
- 在pom.xml的<dependencies></dependenies>导入依赖
- spring核心、mybatis核心、mybatis-spring整合包、log4j日志、common-mysql-java驱动、Druid数据库连接池、spring-orm整合包、Junit测试,具体看这里:Spring框架用到依赖
2. 新建applicationContext.xml文件
2.1 jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3308/companydb?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=20
2.2 配置连接池、SqlSessionFactory
配置DataSource、SqlSessionFactory
<?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/p
http://www.springframework.org/schema/p/spring-p.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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd "
>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="${jdbc.init}"/>
<property name="maxWait" value="60000"/>
<property name="timeBetweenEvictionRunsMillis" value="50000"/>
<property name="minEvictableIdleTimeMillis" value="3000"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/lyx/dao/*.xml</value>
</list>
</property>
<property name="typeAliasesPackage" value="com.lyx.entity"></property>
</bean>
</beans>
2.3 写个UserDao-Mapper.xml映射文件
- 注意文件头,与Mybatis-config.xml文件头不一致
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lyx.dao.UserDao">
<resultMap id="user_resultMap" type="User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="gender" property="gender"></result>
<result column="regist_time" property="registTime"></result>
</resultMap>
<select id="queryUsers" resultMap="user_resultMap">
select id,username,password,gender,regist_time
from t_user
</select>
</mapper>
2.4 写个测试类
public class TestSpringMybatis {
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)context.getBean("sqlSessionFactory");
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.queryUsers();
for (User user : users) {
System.out.println(user);
}
}
}
运行报错:.xml文件在编译时不能被编译到测试文件中,所以运行过程中找不到我注册的.xml文件,从而导致我上面再UserDao-Mapper.xml文件中定义的<mapper namespace=“com.lyx.dao.UserDao”/>没有注册 运行成功
2.5 配置MapperScannerConfigurer
理解:帮助我们把Mybatis的Dao实现放到工厂中,我们知道,用来mybatis之后,我们是不写UserDaoImpl这种实现类的,我们要想办法把这个实现类对象放到工厂中
作用:管理Dao实现类的创建,并创建Dao对象,存入工厂管理
- 扫描所以DAO接口,去构建DAO实现
- 将DAO实现存入工厂管理
- DAO实现对象在工厂中的id是:“首字母小写的-接口的类名”,例如UserDao==>userDAO,OrderDAO==>orderDAO
<bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lyx.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
2.6 配置Service
<bean id="userService" class="com.lyx.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
- 注意ref中的值是对应DAO接口的首字母小写的接口名
2.7 测试一下
@Test
public void test2(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserService userService = (UserService)context.getBean("userService");
List<User> users = userService.queryUsers();
for (User user : users) {
System.out.println(user);
}
}
3. 事务【重点】
3.1 配置DataSourceTransactionManager
- 事务管理器,其中ref=dataSource,可以控制事务功能(commit,rollback等)就是对访问或者操作dataSource里面配置的数据库时,进行事务控制
- 注意DataSourceTransactionManager和SqlSessionFactoryBean要注入同一个DataSource的Bean,否则事务控制失败!
代码如下:
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
3.2 配置事务通知
- 基于刚刚的事务管理器,我们为其配置一个事务通知,生成一个额外功能,advice,此advice可以切入任何需要事务的方法,通过事务管理器为方法控制事务
- 代码如下
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<tx:method name="queryUsers" read-only="false" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="query*" propagation="SUPPORTS" timeout="-1" no-rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
- name里面的query*,意思是所有以query开头的方法,切入事务控制
3.3 事务属性
- 上面的事务属性的几个标识,依次为只读,事务隔离级别,需要回滚的异常类,事务传播属性,超时(一般不用),不需要回滚的异常类
- 下面挑几个侧重讲解
3.3.1 隔离级别
isolation
isolation的值有5种:
- default 默认值
- read-uncommitted 读未提交
- read-commited 读提交
- repeatable-read 可重复读
- serialized-read 序列化读
以上5种值的隔离级别依次升高!
事务isolation这个属性的作用是什么?事务并发时的安全问题:
- 丢失更新(lost update):在完全未隔离事务的情况下,两个事物更新同一条数据资源,如果其中一个事务异常终止,回滚造成第一个完成的更新也同时丢失。
- 脏读(dirty read):一个事务查询到另一个事务还未提交的更新数据,read-commited读提交可防止
- 幻影读(phantom read):一个事务多次读取,每一次读取结果都不一样,由于是另一个事务在这期间做了插入或者删除了数据造成的。serialized-read可防止
- 不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。repeatable-read 可重复读可防止
- 丢失更新2(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。
以上5个值,我们默认使用default,当我们是Mysql数据库,spring默认使用可重复读repeatable-read,当我们是Oracle数据库,spring默认使用读提交read-commited,这两个也是安全性比较高的 如果我们不写isolation属性,默认也是default
3.3.2 传播行为
propagation
- 当事务涉及到事务嵌套,Service调用Service时,可以会存在问题
两个Service层的每个方法都要自己的事务,当Service1中的方法调用了Service2中的方法,就会导致事务的嵌套,如果Service2中的方法发生了错误,该方法事务回滚,但是对调用它的Service1中的方法却没有进行回滚 - 传播行为可以为我们解决
- 有两个值,SUPPORTS,REQUIRED
- SUPPORTS:当不存在外部事务,则该方法也不开启事务,存在外部事务,则合并到外部事务中,比如我们的查询,不对数据库操作,只是查找的话,基本上不会开启事务,所以SUPPORTS适合查询方法使用
- REQUIRED:当不存在外部事务,则开启新事务,存在外部事务,则合并到外部事务中,比如我们的增删改,对数据库进行操作,肯定要存在事务控制,所以REQUIRED 适合增删改
3.3.3 timeout事务超时
当事务所需要存在的涉及被其他事务占用,则等待 -1 就是数据库指定等待时间 自定义也可以时间,用到不多
3.3.4 readonly
- true 只读,可提供查询效率 ,适合查询
- false 可读可写,适合增删改
3.3.5 事务回滚rollback-for
- 如果事务中抛出,RuntimeException,则自动回滚
- 但是如果事务中抛出,CheckException(非运行时异常 Exception),不会自动回滚,而是默认提交事务
- 怎么解决不会自动回滚的异常呢?处理方案:将CheckException转换成RuntimeException上抛,或者 设置 rollback -for=“exception”
3.4 事务编织
将刚刚的事务的Advice的id切入需要事务的业务方法中,这就是Spring的AOP
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<tx:method name="queryUsers" read-only="false" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="query*" propagation="SUPPORTS" timeout="-1" no-rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.lyx.service.UserServiceImpl.*queryUsers())"/>
<aop:advisor advice-ref="txManager" pointcut-ref="pc"/>
</aop:config>
5. applicationContext.xml文件所有内容
<?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/p
http://www.springframework.org/schema/p/spring-p.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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd "
>
<bean id="userService" class="com.lyx.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="${jdbc.init}"/>
<property name="maxWait" value="60000"/>
<property name="timeBetweenEvictionRunsMillis" value="50000"/>
<property name="minEvictableIdleTimeMillis" value="3000"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations">
<list>
<value>classpath:com/lyx/dao/*.xml</value>
</list>
</property>
<property name="typeAliasesPackage" value="com.lyx.entity"></property>
</bean>
<bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lyx.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txManager" transaction-manager="tx">
<tx:attributes>
<tx:method name="queryUsers" read-only="false" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="query*" propagation="SUPPORTS" timeout="-1" no-rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.lyx.service.UserServiceImpl.*queryUsers())"/>
<aop:advisor advice-ref="txManager" pointcut-ref="pc"/>
</aop:config>
</beans>
|