整合mybatis和spring,像一个框架一样使用
使用的技术是IOC
- 为什么ioc能把俩个框架集成在一起?
是因为ioc能够创建对象。 - 可以把mybatis框架中的对象交给spring统一创建,开发人员就不用同时面对两个或多个框架了,只需要面对一个spring。
mybatis的使用
定义dao接口,
定义mapper文件
创建dao代理对象 dao.getMapper(StudentDao.class)
- 主配置文件
- 数据库信息
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
我们会使用独立的连接池类替换mybatis默认自己带的,把连接池对象也交给spring创建。
- mapper文件位置
<mappers>
<mapper resource="com/neuedu/dao/IUserDao.xml"/>
<mapper resource="com/neuedu/dao/DeptDao.xml"/>
</mappers>
以上可得我们需要创建的对象
独立的连接池对象, 使用阿里的druid连接池
sqlSessionFactory对象
创建dao对象
整合过程
- 新建maven项目
- 加入maven的依赖
spring依赖
mybatis依赖
mysql驱动
spring事务的依赖
mybatis和spring集成的依赖:mybatis官方提供的,用来在spring项目中创建mybatis的SqlSessionFactory。dao对象的 - 创建实体类
- 创建dao接口和mapper文件
- 创建mybatis主配置文件
- 创建spring的配置文件:声明mybatis的对象交给spring创建
数据源
sqlSessFactory
...
依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mybatis spring整合-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- 阿里的连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<build>
<!-- 目的是把src/main/java下的xml文件输出到classes中-->
<resources>
<resource>
<!-- 所在目录-->
<directory>src/main/java</directory>
<!-- includes中的.properties 和 .xml都会被扫描到-->
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
Druid连接池
https://github.com/alibaba/druid
- Spring druid连接池的配置参考地址
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE
- 通用配置
DruidDataSource大部分属性都是参考DBCP的,如果你原来就是使用DBCP,迁移是十分方便的。
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
在上面的配置中,通常你需要配置url、username、password,maxActive这三项。
Druid会自动跟url识别驱动类名,如果连接的数据库非常见数据库,配置属性driverClassName
asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间
在spring配置文件中找其他配置文件需要用classpath:...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/java_ssm"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory01">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!-- 连接dao对象
在内部调用getMapper 生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory01"/>
<!-- 把所有接口都创建好对象
多个包用逗号隔开
-->
<property name="basePackage" value="com.lz.dao"/>
</bean>
<!-- 引入service-->
<bean class="com.lz.service.impl.AccountServiceImpl" id="accountService">
<property name="ac" ref="accountDao"/>
</bean>
</beans>
- test类
public class MyTest {
String config = "ApplicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
@Test
public void myTest01(){
AccountServiceImpl service = (AccountServiceImpl) ac.getBean("accountService");
Account account = new Account();
account.setMoney(100000);
account.setName("人生");
service.insertInto(account);
}
@Test
public void test02(){
AccountServiceImpl service = (AccountServiceImpl) ac.getBean("accountService");
List<Account> all = service.findAll();
for(Account a : all){
System.out.println(a);
}
}
}
- druid连接池
- 配置工厂
- 配置dao包自动创建对象
使用properties文件配置连接池时一定要加jdbc.
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java_ssm
jdbc.username=root
jdbc.password=root
<!-- 扫描jdbc配置文件的位置-->
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
spring的事务处理
什么是事务?
在数据库中,提出了事务。事务是指一组sql语句的集合,集合中有多条sql语句,我们希望多个sql语句都能成功,或者都失败,这些sql语句的执行是一致的,作为一个整体执行。在什么时候想到使用事务
当操作涉及到多个表,或者是多个sql语句的增删改,需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。通常使用JDBC访问数据库,还是mybatis访问数据库怎么处理事务
jdbc: conn.commit(); conn.rollback();
mybatis: sqlsession.commit(); sqlsession.rollback();
hibername访问数据库:session.commit(); session.rollback();上个问题中事务的处理方式,有什么不足
1.不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理。
2.掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回顾事务。
3.处理事务的多种方法。
总结就是多种数据库的访问技术,有不同的事务处理机制,对象,方法。怎么解决不足
spring提供一种事务处理的统一模型,能使用统一步骤,完成多种不同数据库访问技术的事务处理。
使用spring的事务处理机制,可以完成mybatis等访问数据库的事务处理。
处理事务,需要怎么做,做什么
把事务信息交给spring就可以了
1.事务内容提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他众多的实现类
接口:PlatformTransactionManager,定义了事务重要方法commit, rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库-- spring创建好的是DataSourceTransactionManager
怎么使用: 你需要告诉spring 你用的是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器的实现类,在spring的配置文件中使用<bean>声明就可以了。
例如:
<bean id="xxx" class="....DataSourceTransactionManager">
2.你的业务需要什么样的事务,说明事务类型。
1. 事务的隔离级别: 都是以ISOLATION_XXX
myql默认是REPEATABLE_READ(可重复读), oracle默认READ_COMMITTED(读以提交)。
DEFAULT:采用DB默认的事务隔离级别。
READ_UNCOMMITTED:读未提交。
READ_COMMITTED:读以提交 解决脏读,存在不可重复读与幻读。
REPEATABLE_READ:可重复读 解决脏读、不可重复读,存在幻读。
SERIALIZABLE:串行化。不存在并发问题。
2. 事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。单位时秒,整数值, 默认是 -1。
- 事务的传播行为 7种
PROPAGATION_REQUIRED:如果存在事务,就使用当前事务,如果没有就新建事务。
- 事务提交事务,回滚事务的时机
- 当你的业务方法执行成功,没有异常抛出,spring在方法执行后提交事务。事务管理器commit
- 当你的业务方法抛出运行时异常,spring执行回滚,调用事务管理器的rollback。
- 当你的业务方法抛出非运行时异常,主要是受差异常,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException、 SQLException
- 总结spring事务
管理事务的是 事务管理器和它的实现类
spring 的事务是一个统一模型
指定要需要的事务管理器实现类,使用<bean>
指定那些类,哪些方法需要加入事务的功能
指定方法需要的隔离级别,传播行为,超时。
spring框架中提供的事务处理的方案
适合中小项目使用的,注解方案
@Transactional注解增加事务
放在public方法上代表事务
属性:
propagation: 事务传播行为。
isolation:用于设置事务的隔离级别。该属性类型为:Isolation枚举。
rollbackFor:指定需要回归的异常类。
rollbackForClassName:
noRollbackFor:指定哪些不回滚
- 使用步骤
- 声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
- 开启事务注解驱动,告诉spring框架, 我要使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知。
@Around("你要增加的事务功能的业务方法名称")
Object myAround(){
try{
method();
spring事务管理器.commit()
}catch(Exception e){
apring事务管理器的rollback()
}
}- 在你的方法的上面加入@Trancational
- spring事务处理
spring的配置文件
<?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:context="http://www.springframework.org/schema/context" 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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 声明mybatis提供的sqlsessionFactoryBean类,这个类内部创建sqlSessionFactory的-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- set注入把数据库连接池赋给了dataSource-->
<property name="dataSource" ref="dataSource"/>
<!-- mybatis主配置文件的位置
configLocation 是Resource类型, 读取配置文件的
它的赋值,使用value,指定文件的路径,使用classpath:表示文件位置-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.lz.dao"/>
</bean>
<bean class="com.lz.service.impl.BuyGoodServiceImpl" id="buyService">
<property name="sd" ref="saleDao"/>
<property name="gd" ref="goodsDao"/>
</bean>
<!-- spring的事务处理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 指定数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
注解开启事务
public class BuyGoodServiceImpl implements BuyGoodsService {
private GoodsDao gd;
private SaleDao sd;
/**
* rollbackfor : 指定异常回滚
* @param goodId
* @param nums
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class,
NotClassException.class,
RuntimeException.class
}
)
@Override
public void buy(int goodId, int nums) {
// 记录销售信息
Sales sales = new Sales();
sales.setGid(goodId);
sales.setNums(nums);
sd.insertSale(sales);
// 更新库存
Goods goods = gd.selectGoods(goodId);
if(goods == null){
throw new NullPointerException("编号是+"+ goodId +"商品不存在");
} else if(goods.getAmount() < nums){
throw new NotClassException("商品库存不足");
}
// 修改库存
Goods goods1 = new Goods();
goods1.setId(goodId);
goods1.setAmount(nums);
gd.updateGoods(goods1);
}
public void setGd(GoodsDao gd) {
this.gd = gd;
}
public void setSd(SaleDao sd) {
this.sd = sd;
}
}
测试类
public class MyTest {
private String config = "applicationContext001.xml";
private ApplicationContext ac = new ClassPathXmlApplicationContext(config);
@Test
public void Test01(){
BuyGoodsService service = (BuyGoodsService) ac.getBean("buyService");
System.out.println(service.getClass().getName());
service.buy(1005, 100);
}
}
- 遇到的问题
mysql数据库默认引擎是MyISAM,它不支持事务; 所以改成常用的innorDB 支持事务。
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
RuntimeException.class
}
和@Transactional 等效 都是默认的
- 默认抛出运行时异常
- 可以放在公共类和方法上(public)
- rollbackFor:
处理逻辑:
1. spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
如果异常在rollbackFor列表中,不管时什么类型的异常一定会回滚。
2. 如果异常类型不在列表中,则看是不是runtiomException异常,如果是则回滚。
适合大型项目,有很多的类,方法,需要大量配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要事务。这种方式业务和事务配置完全分离。
实现步骤: 都是在xml配置文件中实现。
1. 加入依赖aspectj
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
- 声明事务管理对象
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="manager"/>
- 声明方法需要事务的类型(配置方法的事务属性(隔离级别,传播行为,超时))
- 配置aop:指定哪些类需要代理。
- spring xml配置
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.lz.dao" />
</bean>
<bean id="buyService" class="com.lz.service.impl.BuyGoodServiceImpl">
<property name="sd" ref="saleDao"/>
<property name="gd" ref="goodsDao"/>
</bean>
<!-- 声明事务管理对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 声明业务方法事务属性(隔离级别,传播方式)
id: 自定义名称, 表示<tx:advice> 和 </tx:advice>之间的配置内容
transaction-manager: 表示事务管理器对象id
-->
<tx:advice transaction-manager="transactionManager" id="myAdvice">
<!-- 表示要配置事务的属性-->
<tx:attributes>
<!-- 表示给具体的方法配置属性
name : 完整的方法名
方法名可以用通配符 *表示任意字符
其他属性和之前的意义
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,
com.lz.execp.NotClassException"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
<!-- 配置切入点表达式, 指定哪些包中类,要使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
-->
<aop:pointcut id="servciePt" expression="execution(* *..service..*.*(..))"/>
<!--
配置增墙器:
关联advice和pointcut
advice-ref: 通知 , 上面的tx:advice那里的配置
pointcut-ref: 切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servciePt"/>
</aop:config>