这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
Spring集成Mybatis
集成思路
Spring能集成很多的框架,是Spring的优势,通过集成其他框架试开发更简单方便,集成使用的SPring的IOC功能
要使用Mybatis步骤
要使用mybatis就要创建mybatis框架里的某些对象,使用这些对象就能使用mybatis的功能了,到底需要哪些呢,我们可以把这些对象交给Spring来管理,需要使用在容器里拿就可以了
- 需要有dao的代理对象
- 需要SQLSessionFactory,创建sqlSessionFactory对象,才能使用openSession()得到SqlSession对象
- 数据源DataSource对象
整合mybatis
生成数据库表
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
加入依赖xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.12</version>
</dependency>
<!--spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.12</version>
</dependency>
<!--springjdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.12</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--Mybatis-spring依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--mysql连接数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--druid依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>
实体类
public class User {
private int id; //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 + ''' +
'}';
}
}
UserMapper.java
@Mapper
public interface UserMapper {
public int InsertUser(User user);
public User finfdById(Integer id);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定mapping接口-->
<mapper namespace="org.study.Mapper.UserMapper">
<insert id="InsertUser" >
INSERT INTO user
( id, name,pwd)
VALUES(#{id}, #{name}, #{pwd});
</insert>
<select id="finfdById" resultType="user">
select * from user where id= #{id}
</select>
</mapper>
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void insertUser(User user) {
userMapper.InsertUser(user);
}
@Override
public User findById(Integer id) {
User user = userMapper.finfdById(id);
return user;
}
}
@Service
public interface UserService {
public void insertUser(User user);
public User findById(Integer id);
}
Mybatis配置文件
<?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>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<typeAliases>
<package name="org.study.domain"/>
</typeAliases>
<mappers>
<package name="org.study.Mapper"/>
</mappers>
</configuration>
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"
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">
<!--声明数据源-->
<context:annotation-config />
<context:component-scan base-package="org.study"/>
<bean id="MyDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC"></property>
<property name="name" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--声明SqlSessionFactoryBean 在这个类的内部创建 SqlSessionFactory-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定数据源-->
<property name="dataSource" ref="MyDataSource"></property>
<!--指定mybatis主配置文件-->
<property name="configLocation" value="classpath:mybatsi-config.xml"></property>
</bean>
<!--声明MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactoryBean-->
<property name="sqlSessionFactoryBeanName" value="factory"></property>
<!--知道包名-->
<property name="basePackage" value="org.study.Mapper"></property>
</bean>
</beans>
测试类
@Test
public void shouldAnswerWithTrue()
{
String config ="application.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
UserService service = (UserServiceImpl) applicationContext.getBean("userServiceImpl");
User user1 = new User();
user1.setId(13);
user1.setName("liming");
user1.setPwd("12345");
service.insertUser(user1);
User user = service.findById(13);
System.out.println(user);
}
输出结果
Spring事务
概述
事务是一些列SQL语句的集合,是多条SQL要么都成功要么都失败。
什么时候使用事务
一个操作需要多条Sql语句一块完成,操作才能成功
事务在哪里说明
在业务方法上边
public class dao(){
public void insertA();
public void insertB():
}
public void Service(){
@Autowired
private dao dao1;
//事务下在这里
public void insertAB(){
dao1.insertA();
dao1.insertB();
}
}
Spring事务管理器
不同的数据库访问技术,处理事务是不同的,Spring统一管理事务,把不同的数据访问技术的事务处理统一起来,使用Spring的事务管理器,管理不同数据库访问技术处理事务,只需要掌握spring的事务处理一个方案,可以实现使用不同数据库访问技术的事务管理。
spring框架使用事务管理器管理对象,事务管理器的接口是PlatformTransacationManger,定义了事务的操作,主要是commit()rollback()事务管理器有很多实现类,一种数据库访问计数有一个实现类,有实现类具体完成事物的提交,回滚。 jdbc和mmybatis的事务管理器是DataSourceTranactionManager。hibernate事务管理器是hibernateTranactionManager
老师画的一张图,spring管理事务的工作方式,业务代码正常执行局提交了,运行时出现异常就回滚了
业务代码正常执行局提交了,运行时出现异常就回滚了
异常分类
- Error 严重错误,回滚事务
- Exception 异常类,可以处理的异常
- 运行时异常 : RuntimeException和它的子类都是运行时异常,在程序执行中抛出的异常,常见的有 NullPointException空指针异常,IndexOutOfBoundsException数组越界异常ClassCastException强制装换异常
- 受查异常: 编写java代码的时候,必须出来的异常,如IOException。SQLException
运行方法中只要出现了运行时异常事务回滚,其他情况(正常执行方法,受查异常)提交事务
事务使用的是AOP的环绕通知
环绕通知: 可以在目标方法的前后都机上增强,不需要修改代码,在业务代码前开启事务,在业务代码后提交事务,出现异常Catch回滚事务
事务定义接口 TransactionDefinition
定义了三类常量,定义了有关事务控制的属性
- 隔离级别
- 传播行为
- 事物的超时
隔离级别
隔离级别:控制事物之间的影响程度
这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。
- DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ(可重复读);Oracle默认为 READ_COMMITTED。(读已提交)
- READ_UNCOMMITTED:读未提交。未解决任何并发问题。
- READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
- REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
- SERIALIZABLE:串行化。不存在并发问题。
超时时间
以秒为单位 ,默认-1,表示一个业务方法最长的执行时间,到时见没有执行完毕,会回滚事务
传播行为
业务方法在执行时,事务在方法间的传递和使用,可以标志方法有无事务,有七个值PROPAGATION_XXXX开头
-
PROPAGATION_REQUIRED 默认传播行为,方法执行时,如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务,在新事务里执行
-
PROPAGATION_SUPPORTS 支持,如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
-
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
-
PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
-
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
-
PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
-
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
代码测试--不加事务
模拟买东西订单表添加商品,库存表减库存
准备两张表
CREATE TABLE `sale` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`gid` int(11) DEFAULT NULL,
`nums` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
CREATE TABLE `goods` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`amount` int(11) DEFAULT NULL,
`price` float DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8
实体类
@Data
public class Sale {
private Integer id;
private Integer gid ;
private Integer nums;
}
@Data
public class Goods {
private Integer id;
private String name;
private Integer amount;
private float price;
}
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:annotation-config />
<context:component-scan base-package="com.study"/>
<bean id="MyDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC"></property>
<property name="name" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--声明SqlSessionFactoryBean 在这个类的内部创建 SqlSessionFactory-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定数据源-->
<property name="dataSource" ref="MyDataSource"></property>
<!--指定mybatis主配置文件-->
<property name="configLocation" value="classpath:mybatsi-config.xml"></property>
</bean>
<!--声明MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactoryBean-->
<property name="sqlSessionFactoryBeanName" value="factory"></property>
<!--知道包名-->
<property name="basePackage" value="com.study.mapper"></property>
</bean>
</beans>
mybatis配置文件
<?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>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<typeAliases>
<package name="com.study.domain"/>
</typeAliases>
<mappers>
<package name="org.study.Mapper"/>
</mappers>
</configuration>
mapper及xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定mapping接口-->
<mapper namespace="com.study.mapper.saleMapper">
<insert id="buyGoods" >
insert into sale ( gid, nums)
VALUES(#{gid}, #{nums});
</insert>
</mapper>
@Mapper
public interface saleMapper {
public void buyGoods(Sale sale1);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定mapping接口-->
<mapper namespace="com.study.mapper.goodsMapper">
<select id="findById" resultType="goods">
select * from goods where id=#{id}
</select>
<update id="updateAmount" >
update goods set amount = amount - #{amount} where id= #{id}
</update>
</mapper>
@Mapper
public interface goodsMapper {
public Goods findById(Integer id);
public void updateAmount(Goods good);
}
业务方法
public interface BuyService {
void Buy(Integer id,Integer num);
}
@Autowired
private goodsMapper goodsMapper1;
@Autowired
private saleMapper saleMapper1;
@Override
public void Buy(Integer id, Integer num) {
System.out.println("开始买了");
Sale sale = new Sale();
sale.setGid(id);
sale.setNums(num);
saleMapper1.buyGoods(sale);
Goods byId = goodsMapper1.findById(sale.getGid());
if (byId == null){
throw new NullPointerException("商品不存在");
} else if(byId.getAmount()<sale.getNums()){
throw new MyException("库存不足");
}
Goods goods = new Goods();
goods.setAmount(sale.getNums());
goods.setId(sale.getGid());
goodsMapper1.updateAmount(goods);
}
测试类
public void shouldAnswerWithTrue()
{
String config ="application.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
BuyService service = (BuyService) applicationContext.getBean("buyServiceImpl");
service.Buy(1001,1000);
}
以上代码我没加事务的情况下 去购买没有的产品,报错但是第一步订单表的信息还是会保存。
Spring框架使用自己的注解@Transaction控制事务
@Transaction使用注解的属性控制事务,隔离级别,传播行为,超时时间
@Transaction的属性
- propagation : 事物的传播行为,他使用的Propagation类的枚举值 Propagation.REQUIRED
- isolation : 隔离级别 使用Isolation枚举类表示隔离级别 默认Isolation.DEFAULT
- readOnly : Boolean类型的值 标识数据库操作是不是只读,默认false
- timeout : 事务超时,默认是-1 整数值单位是秒,例如 timeout =20
- rollBackFor :表示回滚的异常类型
- rollBackForClassName : 回滚的异常类列表。值时异常类名称,String类型的值
- noRollackFor :不需要回滚的数据异常,是class类型
- noRollBackForClassName : 不需要回滚的数据异常,是String类型 位置
- 在业务方法的上边,在public方法上边
- 在类的上边 特点
- Spring自己的事务管理
- 适合中小项目
- 使用方便
使用注解实现事务
在Spring配置文件里声明事务管理器并开启注解
<!--声明事务控制器-->
<bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定事务操作的数据源-->
<property name="dataSource" ref="MyDataSource"></property>
</bean>
<!--开启事务注解驱动,告诉框架使用注解管理事务
transaction-manager:指定事务管理器的id
proxy-target-class:为true则是基于类的代理将起作用(需要cglib库),为false或者省略这个属性
-->
<tx:annotation-driven transaction-manager="TransactionManager" proxy-target-class="true"></tx:annotation-driven>
在业务代码上添加@Transactional注解并配置属性,直接写@Transactional也可以实现事务,都使用默认值。rollbackFor回滚的异常类型,指定的话先根据指定的找,找不到找是不是RuntimeException的子类都会回滚,那这个属性是不是有点多余,当我们要指定受查异常时就可以使用,还有一个属性noRollackFor,可以指定什么异常不需要回滚
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = 20,
readOnly = false,
rollbackFor ={NullPointerException.class,MyException.class}
)
声明式事务
<!--声明式事务-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="MyDataSource"></property>
</bean>
<!--声明业务方法的事务属性(隔离级别,传播行为,超时)-->
<tx:advice id="Advice" transaction-manager="transactionManager">
<!--给具体的业务方法增加事物的说明-->
<tx:attributes>
<!--
给具体的业务方法添加其他事务属性
name:业务方法名称 配置name的值
1 业务方法的名称
2 带有部分通配符的业务方法的名称
3 使用*
propagation : 指定传播行为的值
isolation :隔离级别
read-only :是否只读
timeout :超时时间
rollback-for :指定回滚的异常类型
-->
<tx:method name="Buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="20"/>
<!--有命名规则的业务方法-->
<tx:method name="add*" propagation="REQUIRED"></tx:method>
<tx:method name="update*" propagation="REQUIRED"></tx:method>
<tx:method name="delect*" propagation="REQUIRED"></tx:method>
<!--以上方法以外的* -->
<tx:method name="*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<aop:config>
<!--声明切入点表达式
expression :声明那些类中的方法参与事务
id 名称
-->
<aop:pointcut id="servicePoint" expression="execution(* *..service..*.*(..))"/>
<!--切入点表达式和事务通知-->
<aop:advisor advice-ref="Advice" pointcut-ref="servicePoint"></aop:advisor>
</aop:config>