1、Spring JdbcTemplate
代码示例:
首先导入pom依赖:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhoulz</groupId>
<artifactId>spring_aop_tx</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--添加的maven依赖-->
<!--添加的spring-context依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--添加junit的依赖-->
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--添加的spring-aop依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--cglib依赖-->
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!--aspectj weaver依赖 —— 提供了一种功能更强的织入方式-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
<!--<scope>runtime</scope>--> <!--这个一定要注释掉,不然@Aspect出不来-->
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--除aop相关的一些依赖外,还要导入以下依赖:-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!--最后,因为和数据访问有关,还要导入JDBC、ORM、Transaction相关的依赖。这里已经自动导入了,因为依赖关系-->
</dependencies>
</project>
然后,resources文件夹下,创建db.properties: (demo是对应的数据库)
jdbc.username=root
jdbc.password=123456
idbc.url=jdbc:mysql://localhost:3306/demo
jdbc.driverName=com.mysql.jdbc.Driver
然后,resources文件夹下,创建applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--后加:-->
<!--注解和配置文件是组合使用的:-->
<!--如:这里EmpDao的注册用的即使注解,这里是扫描的操作,然后对应类及属性加上注解即可-->
<context:component-scan base-package="com.zhoulz"></context:component-scan>
<!--上面的context命名空间和下面这行代码导入进来后,下面输入时才有提示-->
<context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true"></context:property-placeholder>
<!--这里,需要在后面加上属性:ignore-unresolvable="true",不然test01()中连接不上数据库-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driverName}"></property>
<property name="url" value="${jdbc.url}"></property>
</bean>
<!--spring 为我们提供了一个类:JdbcTemplate,它是属于包package org.springframework.jdbc.core;的,即属于spring里面的东西 -->
<!--先把JdbcTemplate注册为Bean对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--下面的dataSource这个属性被要求有,必须定义-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--首先,要声明/添加 事务管理器--><!--即:配置事务管理器的bean对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--然后,接着还要导入tx的命名空间,见上面-->
<!--再然后,就可以引入tx的一些注解了:--> <!--即:开启基于注解的事务管理器的配置-->
<!--注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--以前的AOP是这样的,同样的操作-->
<!--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
<!--添加好事务管理器就可以引入相关的事务控制了,即:在BookService类的buyBook()方法上面添加上@Transactional注解即可-->
</beans>
接下来对数据库中的数据进行操作:增删改查
Dao 接口:
package com.zhoulz.dao;
import com.zhoulz.entity.Emp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public interface Dao {
@Autowired
public void save(Emp emp);
}
接口对应的实现类:EmpDao
package com.zhoulz.dao;
import com.zhoulz.entity.Emp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class EmpDao implements Dao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save(Emp emp){ //测试见test08()
String sql = "insert into emp(empno,ename) value(?,?)";
int update = jdbcTemplate.update(sql, emp.getEmpno(), emp.getEname());
System.out.println(update);
}
}
entity文件夹下的 Emp 类 (常规代码)—— 对应数据库?
package com.zhoulz.entity;
import java.util.Date;
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
public Emp() {
}
public Emp(Integer empno, String ename) {
this.empno = empno;
this.ename = ename;
}
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getComm() {
return comm;
}
public void setComm(Double comm) {
this.comm = comm;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", comm=" + comm +
", deptno=" + deptno +
'}';
}
}
测试类:MyTest
import com.alibaba.druid.pool.DruidDataSource;
import com.zhoulz.dao.EmpDao;
import com.zhoulz.entity.Emp;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MyTest {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test01() throws SQLException {
DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class);
System.out.println(dataSource.getConnection()); //测试没成功
//System.out.println(dataSource.getClass());
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
System.out.println(jdbcTemplate);
}
//测试对数据的增删改查 —— 加入单条数据
@Test
public void test02(){
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
//不能直接jdbcTemplate.insert,也没有提示有insert这个方法
//应该先写sql语句
String sql = "insert into emp(empno,ename) value(?,?)";
int zhangsan = jdbcTemplate.update(sql, 1111, "zhangsan");
System.out.println(zhangsan);
}
//批量加入数据
@Test
public void test03(){
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into emp(empno,ename) value(?,?)";
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[]{2222,"lisi"});
list.add(new Object[]{3333,"wangwu"});
list.add(new Object[]{4444,"maliu"});
//int zhangsan = jdbcTemplate.update(sql, 1111, "zhangsan");
int[] result = jdbcTemplate.batchUpdate(sql, list);
for (int i : result) {
System.out.println(i);
}
}
//删除数据
@Test
public void test04(){
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
//String sql = "insert into emp(empno,ename) value(?,?)";
String sql = "delete from emp where empno = ?";
int zhangsan = jdbcTemplate.update(sql, 1111);
System.out.println(zhangsan);
}
//修改数据
@Test
public void test05(){
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
//String sql = "insert into emp(empno,ename) value(?,?)";
String sql = "update emp set ename = ? where empno = ?";
int zhangsan = jdbcTemplate.update(sql,"zhouzhou",2222);
System.out.println(zhangsan);
}
//查询数据 (稍微复杂点)—— 用的是query()
//查询一条数据
@Test
public void test06(){
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select * from emp where empno = ?";
Emp result = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(Emp.class),7902);
System.out.println(result);
}
//查询多条数据(集合)
@Test
public void test07(){
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select * from emp where sal > ?";
List<Emp> result = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(Emp.class),1500);
//System.out.println(result);
for (Emp emp : result) {
System.out.println(emp);
}
}
@Test
public void test08(){
EmpDao empDao = context.getBean("empDao", EmpDao.class);
empDao.save(new Emp(1111,"zhangsan2"));
}
}
2、声明式事务
参考:0 - Spring —— 相关_哆啦A梦的_梦的博客-CSDN博客
(四、java要知道 ——> 1、声明式事务)daimashili
(1)— (8)声明式事务 设置、属性等
代码示例:
首先,resources层下applicationContext.xml的配置见上。
dao层下的 BookDao类:
package com.zhoulz.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//方法
//1、减去某个用户的余额
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where userName=?";
jdbcTemplate.update(sql,price,userName);
}
//2、按照图书的id来获取图书的价格
public int getPrice(int id){
String sql = "select price from book where id = ?";
return jdbcTemplate.queryForObject(sql,Integer.class,id); //Integer.class ???
}
//3、减库存,减去某本书的库存
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
}
}
service层下的 BookService 类:
package com.zhoulz.service;
import com.zhoulz.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import java.awt.print.Book;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
private BookDao bookDao;
/**声明式事务的相关参数:
*
* 1、propagation:传播特性:表示不同的事务之间执行的关系
* 2、ioslation:隔离级别:4种隔离级别,会引发不同的数据错乱问题
* (上面的1/2较为复杂)
*
* 3、timeout:超时时间(s):如果事务超过所设置的超时时间的话,则事务会进行正常的回滚。
* 即:超过所设置的超时时间时(说明没执行完),事务会正常回滚(即,操作都不执行);
* 而在所设置的超时时间内时,操作都会成功执行。
* 4、readonly:只读事务(默认值false)
* 即:如果配置了只读事务,那么在事务运行期间,不允许对数据进行修改,否则抛出异常。
* 5、noRollBackerfor(设置哪些异常不回滚,即:即使出现异常了,也让它继续执行):
* 6、noRollbackerForClassName:
* (上面两个意思是一样的,只不过传的对象不一样,noRollBackerfor传的是class,noRollbackerForClassName传的是String)
*
* 7、rollBackfor:(设置哪些异常回滚,即操作都不执行)
* 8、rollbackForClassName:
* */
//声明事务: 引入事务控制。然后就可以进行事务回滚等操作
//@Transactional(timeout = 4)
//@Transactional(noRollbackFor = {ArithmeticException.class})
//换个写法:
//@Transactional(noRollbackForClassName = {"java.lang.ArithmeticException"})
//@Transactional(rollbackFor = {FileNotFoundException.class})
//换个写法
//@Transactional(rollbackForClassName = {"java.io.FileNotFoundException"})
//隔离级别 ioslation:
//@Transactional(isolation = Isolation.DEFAULT) ——匹配对应的数据库
//@Transactional(isolation = Isolation.READ_COMMITTED) //读与提交
//@Transactional(isolation = Isolation.READ_UNCOMMITTED)
//@Transactional(isolation = Isolation.REPEATABLE_READ) //可重复读
//@Transactional(isolation = Isolation.SERIALIZABLE) //序列化
@Transactional
public void buyBook() throws FileNotFoundException {
bookDao.getPrice(1);
bookDao.updateBalance("zhangsan",100);
//int i = 1/0; //会导致事务的中断,用来测试
//Thread.sleep(3000);
bookDao.updateStock(1);
new FileInputStream("aaa.txt");
}
}
测试类:MyTest2
import com.zhoulz.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.io.FileNotFoundException;
public class MyTest2 {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test01() throws FileNotFoundException {
BookService bean = context.getBean(/*"bookService",*/BookService.class);
JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
System.out.println(jdbcTemplate);
bean.buyBook();
}
}
(9) 事务的传播特性
(10)测试事务的传播特性
用的比较多的是 :REQUIRED 和 REQUIRES_NEW。
REQUIRED:
如果事务里面包括一些子事务,则最终的操作都是由外部的事务来整体控制(即,如果一个子事务出错了,那么都进行回滚,都没有修改)。这里,外部是指 MultService类下mutl() 方法。如果把外部mutl() 方法的事务去掉(注释掉@Transactional),则实现不了整体控制(里面的子事务互不影响,报错的就回滚,没报错的就正常执行)。
REQUIRES_NEW:
子事务之间,如果声明为REQUIRES_NEW 的话,则互为独立(独立是指先挂起,等其他的执行完了再执行)的。即:异常了就回滚,没异常则正常执行,不受其他子事务异常的影响。
其他的传播特性 —— 待看。
NESTED:—— 常与 REQUIRED 作比较 “嵌套”
代码示例:
dao层下的BookDao类:
package com.zhoulz.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//方法
//1、减去某个用户的余额
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where userName=?";
jdbcTemplate.update(sql,price,userName);
}
//2、按照图书的id来获取图书的价格
public int getPrice(int id){
String sql = "select price from book where id = ?";
return jdbcTemplate.queryForObject(sql,Integer.class,id); //Integer.class ???
}
//3、减库存,减去某本书的库存
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
}
//再加一个方法
//4、更新价格
public void updatePrice(int id){
String sql = "update book set price=price-1 where id=?";
jdbcTemplate.update(sql,id);
}
}
service层下的 BookService 类:
package com.zhoulz.service;
import com.zhoulz.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.awt.print.Book;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@Service
public class BookService {
@Autowired
private BookDao bookDao;
//@Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void buyBook() {
bookDao.getPrice(1);
bookDao.updateBalance("zhangsan",100);
//int i = 1/0; //会导致事务的中断,用来测试
//Thread.sleep(3000);
bookDao.updateStock(1);
//new FileInputStream("aaa.txt");
}
//也写一个updatePrice()方法,调用BookDao中的updatePrice()方法
@Transactional(propagation = Propagation.REQUIRED)
//@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updatePrice(){
bookDao.updatePrice(1);
int i = 1/0;
}
}
service层下的 MultService 类:
package com.zhoulz.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MultService {
@Autowired
private BookService bookService;
@Transactional
public void mult(){
//bookService.updatePrice();
bookService.buyBook();
bookService.updatePrice();
}
}
测试类:MyTest3
import com.zhoulz.service.MultService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest3 {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test01(){
MultService bean = context.getBean(MultService.class);
bean.mult();
}
}
3、基于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:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--分为bean对象的配置和事务的配置-->
<!-- 一、bean对象的配置-->
<!--1、定义扫描包 — 扫描一些基本的包。 这样不用每个bean都单独写-->
<context:component-scan base-package="com.zhoulz"></context:component-scan>
<!--2、进行事务操作的话,必然要连接数据库,所以要把外部数据属性的配置文件加入进来,比如db.properties-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--3、还用到了一个druid(德鲁伊)的数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--然后写上一些对应的属性:-->
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverNamee}"></property>
</bean>
<!--4、还有JdbcTemplate对应的配置:-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 二、事务的配置:-->
<!--1、首先,声明一个事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--声明式事务是依托于AOP的,所以AOP的配置必须要有-->
<!--2、AOP配置-->
<aop:config>
<!--然后是aop中切面之类的-->
<aop:pointcut id="txPointcut" expression="execution(* com.zhoulz.service.*.*(..))"/>
<!--事务建议(这里事务还没配)-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPointcut"></aop:advisor>
</aop:config>
<!--事务的话,有专门的事务标签 (对应上面的事务建议)-->
<!--3、事务标签(然后把下面的myAdvice拿上去)-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--配置事务的属性-->
<tx:attributes>
<!--配置咋哪些方法上添加事务-->
<tx:method name="addBook" propagation="REQUIRED" read-only="true" isolation="DEFAULT"/>
<tx:method name="updatePrice" propagation="REQUIRED"></tx:method>
<!--有通配符的写法:-->
<tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
<tx:method name="update*" propagation="REQUIRES_NEW"></tx:method>
</tx:attributes>
</tx:advice>
</beans>