Java学习笔记-Day68 Spring 框架(六)



一、Spring 事务处理

1、Spring 事务处理简介


本地事务(local transaction):使用单一资源管理器,管理本地资源。

全局事务(global transaction):通过事务管理器和多种资源管理器,管理多种不同类型的资源,如JDBC资源和JMS资源。

编程式事务:编程式事务可以是一个代码段,通过编码方式,开启事务、提交事务、回滚事务。

声明式事务:通过 xml配置或注解 实现事务处理。声明式事务边界可以是一个方法或者一个类中的所有方法。Spring AOP和EJB都是声明式事务。

Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。

编程式事务侵入到了业务代码里面,但是提供了更加详细的事务处理理。而声明式事务由于基于AOP,所以既能起到事务处理的作用,又可以不影响业务代码的具体实现。

Spring事务处理只能处理运行时异常(RuntimeException)。

事务处理都放在业务层。事务处理操作时使用同一个数据库连接对象。

2、Spring 事务处理相关的类和接口


(1)PlatformTransactionManager接口(抽象模型,顶层接口):描述Spring事务策略。

org.springframework.transaction.PlatformTransactionManager

public interface PlatformTransactionManager {
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}


(2)AbstractPlatformTransactionManager抽象类(实现PlatformTransactionManager接口)

org.springframework.transaction.support.AbstractPlatformTransactionManager

@SuppressWarnings("serial")
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
	// 部分方法
	/**
	 * 实现commit的部分方法
	 */
	@Override
	public final void commit(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}
		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		if (defStatus.isLocalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Transactional code has requested rollback");
			}
			processRollback(defStatus, false);
			return;
		}
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			processRollback(defStatus, true);
			return;
		}
		processCommit(defStatus);
	}
	private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;
			try {
				boolean unexpectedRollback = false;
				prepareForCommit(status);
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Releasing transaction savepoint");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					status.releaseHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction commit");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					doCommit(status);
				}
				else if (isFailEarlyOnGlobalRollbackOnly()) {
					unexpectedRollback = status.isGlobalRollbackOnly();
				}
				if (unexpectedRollback) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			catch (UnexpectedRollbackException ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			catch (TransactionException ex) {
				if (isRollbackOnCommitFailure()) {
					doRollbackOnCommitException(status, ex);
				}
				else {
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			catch (RuntimeException | Error ex) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, ex);
				throw ex;
			}
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
			cleanupAfterCompletion(status);
		}
	}	
	protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
	
	/**
	 *  实现rollback的部分方法
	 */
	@Override
	public final void rollback(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}
		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		processRollback(defStatus, false);
	}
	private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;
			try {
				triggerBeforeCompletion(status);
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					status.rollbackToHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
					doRollback(status);
				}
				else {
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
							}
							doSetRollbackOnly(status);
						}
						else {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
							}
						}
					}
					else {
						logger.debug("Should roll back transaction but cannot - no transaction available");
					}
					if (!isFailEarlyOnGlobalRollbackOnly()) {
						unexpectedRollback = false;
					}
				}
			}
			catch (RuntimeException | Error ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
		}
		finally {
			cleanupAfterCompletion(status);
		}
	}
	protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;	
}


(3)DataSourceTransactionManager类(继承AbstractPlatformTransactionManager抽象类)

org.springframework.jdbc.datasource.DataSourceTransactionManager

@SuppressWarnings("serial")
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
		implements ResourceTransactionManager, InitializingBean {
	// 部分方法
	/**
	 * 实现commit的部分方法
	 */
	@Override
	protected void doCommit(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.commit();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}
	/**
	 * 实现rollback的部分方法
	 */
	@Override
	protected void doRollback(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}	
}


(4)TransactionStatus接口:描述事务状态。

org.springframework.transaction.TransactionStatus

public interface TransactionStatus extends SavepointManager, Flushable {
	boolean isNewTransaction();
	boolean hasSavepoint();
	void setRollbackOnly();
	boolean isRollbackOnly();
	@Override
	void flush();
	boolean isCompleted();
}


(5)SavepointManager接口:进行事务回滚的保存点。

org.springframework.transaction.SavepointManager

public interface SavepointManager {
	Object createSavepoint() throws TransactionException;
	void rollbackToSavepoint(Object savepoint) throws TransactionException;
	void releaseSavepoint(Object savepoint) throws TransactionException;
}

3、@Transactional注解


@Transactional:事务处理的注解,可以放在类和方法前,如果是在类前,则是指定该类中的所有方法。通过该注解可以获取同一个数据库连接对象。

@Transactional 注解应该只被应用到 public 方法上。 如果在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系统也不会报错, 但是这个被注解的方法将不会执行已配置的事务设置。

	@Transactional(rollbackFor = Throwable.class)

在这里插入图片描述

如果在接口、实现类、方法上都指定了@Transactional注解,则优先级顺序为 方法 > 实现类 > 接口。

二、Spring 事务处理案例

1、DataSource相关的事务处理案例


题目:增加一个员工,同时添加一个用户,如果增加员工和用户同时成功,则增加操作成功。如果增加员工和用户有一个操作失败,则增加操作失败。如果程序出现了异常,操作也是失败。

在这里插入图片描述

声明式事务 的操作步骤:

(1)导入与事务管理操作相关的jar包。
在这里插入图片描述
(2)获取数据库连接对象。BaseDao类继承JdbcDaoSupport类,在BaseDao类中调用JdbcDaoSupport类的setDataSource方法,给JdbcDaoSupport类的jdbcTemplate赋值。再在BaseDao类中调用JdbcDaoSupport类的getConnection方法,获取数据库连接对象(在不同方法中调用时,返回的都是同一个数据库连接对象)。在JdbcDaoSupport类的getConnection方法中会调用DataSourceUtils类的getConnection方法,而DataSourceUtils类的getConnection方法会对数据库连接对象进行一个简单的注册,保证返回的对象是同一个连接对象。

DataSourceUtils用于管理JDBC,例如数据库连接资源的释放(自动释放)。

在这里插入图片描述

package com.etc.aop.dao;
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;

@Component(value="bd")
public class BaseDao extends JdbcDaoSupport{
    
    
	
	@Autowired
	public void setDataSource(DriverManagerDataSource dataSource) {
    
    
		super.setDataSource(dataSource);
	}
	
	public Connection getConn() throws SQLException {
    
    
		return super.getConnection();
	}
}

(3)员工和用户的实体类。

package com.etc.aop.entity;
import java.util.Date;

public class Staff {
    
    
	private String sno;
	private String name;
	private Date birthday;
	private String address;
	private String tel;
	public Staff(String sno, String name, Date birthday, String address, String tel) {
    
    
		super();
		this.sno = sno;
		this.name = name;
		this.birthday = birthday;
		this.address = address;
		this.tel = tel;
	}
	public Staff() {
    
    
		super();
	}
	public String getSno() {
    
    
		return sno;
	}
	public void setSno(String sno) {
    
    
		this.sno = sno;
	}
	public String getName() {
    
    
		return name;
	}
	public void setName(String name) {
    
    
		this.name = name;
	}
	public Date getBirthday() {
    
    
		return birthday;
	}
	public void setBirthday(Date birthday) {
    
    
		this.birthday = birthday;
	}
	public String getAddress() {
    
    
		return address;
	}
	public void setAddress(String address) {
    
    
		this.address = address;
	}
	public String getTel() {
    
    
		return tel;
	}
	public void setTel(String tel) {
    
    
		this.tel = tel;
	}
	@Override
	public String toString() {
    
    
		return "Staff [sno=" + sno + ", name=" + name + ", birthday=" + birthday + ", address=" + address + ", tel="
				+ tel + "]";
	}
}

package com.etc.aop.entity;

public class Users {
    
    
	private String uname;
	private String sno;
	private String pwd;
	private int role;
	public Users(String uname, String sno, String pwd, int role) {
    
    
		super();
		this.uname = uname;
		this.sno = sno;
		this.pwd = pwd;
		this.role = role;
	}
	public Users() {
    
    
		super();
	}
	public String getUname() {
    
    
		return uname;
	}
	public void setUname(String uname) {
    
    
		this.uname = uname;
	}
	public String getSno() {
    
    
		return sno;
	}
	public void setSno(String sno) {
    
    
		this.sno = sno;
	}
	public String getPwd() {
    
    
		return pwd;
	}
	public void setPwd(String pwd) {
    
    
		this.pwd = pwd;
	}
	public int getRole() {
    
    
		return role;
	}
	public void setRole(int role) {
    
    
		this.role = role;
	}
	@Override
	public String toString() {
    
    
		return "Users [uname=" + uname + ", sno=" + sno + ", pwd=" + pwd + ", role=" + role + "]";
	}
}

(3)Dao接口及其实现类。

package com.etc.aop.dao;
import com.etc.aop.entity.Staff;

public interface StaffDao {
    
    
	public void addStaff(Staff staff) throws Exception;
}	
package com.etc.aop.dao.impl;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.etc.aop.dao.BaseDao;
import com.etc.aop.dao.StaffDao;
import com.etc.aop.entity.Staff;

@Repository(value="sd")
public class StaffDaoImpl implements StaffDao {
    
    
	@Autowired
	private BaseDao bd;

	@Override
	public void addStaff(Staff staff) throws Exception {
    
    
		Connection conn = bd.getConn();	
		System.out.println("staffdao conn:"+conn);
		PreparedStatement pstmt = conn.prepareStatement("INSERT INTO `tstaff` (`sno`, `name`, `birthday`, `address`, `tel`) VALUES (?,?,?,?,?);");
		pstmt.setString(1, staff.getSno());
		pstmt.setString(2, staff.getName());
		pstmt.setDate(3, new Date(staff.getBirthday().getTime()));
		pstmt.setString(4, staff.getAddress());
		pstmt.setString(5, staff.getTel());
		pstmt.executeUpdate();
		pstmt.close();
	}
}
package com.etc.aop.dao;
import com.etc.aop.entity.Users;

public interface UsersDao {
    
    
	public void addUsers(Users user) throws Exception;
}	
package com.etc.aop.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.etc.aop.dao.BaseDao;
import com.etc.aop.dao.UsersDao;
import com.etc.aop.entity.Users;

@Repository(value="ud")
public class UsersDaoImpl implements UsersDao {
    
    
	@Autowired
	private BaseDao bd;

	@Override
	public void addUsers(Users user) throws Exception {
    
    
		Connection conn = bd.getConn();
		System.out.println("userdao conn:"+conn);
		PreparedStatement pstmt = conn.prepareStatement("INSERT INTO `tuser` (`uname`, `sno`, `pwd`, `role`) VALUES (?,?,?,?);");
		pstmt.setString(1, user.getUname());
		pstmt.setString(2, user.getSno());
		pstmt.setString(3, user.getPwd());
		pstmt.setInt(4, user.getRole());
		pstmt.executeUpdate();
		pstmt.close();
	}
}

(4)Service接口及其实现类。

package com.etc.aop.service;
import com.etc.aop.entity.Staff;
import com.etc.aop.entity.Users;

public interface StaffService {
    
    
	public void addStaffUsers(Staff staff,Users user) throws Exception;
}
package com.etc.aop.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.etc.aop.dao.StaffDao;
import com.etc.aop.dao.UsersDao;
import com.etc.aop.entity.Staff;
import com.etc.aop.entity.Users;
import com.etc.aop.service.StaffService;

@Service(value="ss")
public class StaffServiceImpl implements StaffService {
    
    
	@Autowired
	private StaffDao sd;
	
	@Autowired
	private UsersDao ud;
	
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void addStaffUsers(Staff staff, Users user) throws Exception{
    
    
		sd.addStaff(staff);
		ud.addUsers(user);
	}
}

(5)Spring全局配置文件springaop.xml。

添加命名空间:Namespaces -> tx(事务管理)打勾。

在这里插入图片描述

<?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:aop="http://www.springframework.org/schema/aop"
	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 http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	<!-- 使用aop有关的注解支持 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	<!-- 关于IOC注解支持配置 -->
	<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="com.etc.aop"></context:component-scan>
	
	<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
	   <property name="url" value="jdbc:mysql://localhost:3306/empdb?serverTimezone=Asia/Shanghai"></property>
	   <property name="username" value="root"></property>
	   <property name="password" value="root"></property>
	   <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
	</bean>
	<!--事务管理器-->
	<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

三、日志记录

1、日志记录简介


Spring默认有使用日志记录(commoms-logging-1.1.3.jar),可以配置其显示级别。

在这里插入图片描述

显示级别 显示
debug debug、info、warn、error
info info、warn、error
warn warn、error
error error

2、第三方日志记录 log4j


log4j是Apache的一个开源项目,而log4j2是重新架构的一款日志组件,它抛弃了之前log4j的不足,以及吸取了优秀的logback的设计重新推出的一款新组件。

操作步骤:

(1)加入 log4j-1.2.17.jar 到构建路径下。

在这里插入图片描述
(2)添加 log4j.properties 配置文件到src目录下。

log4j.rootCategory=info, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
log4j.category.org.springframework.beans.factory=debug
# 进行日志记录的路径
log4j.category.com.etc.aop.dao.impl=debug

(3)通过 protected final Log logger = LogFactory.getLog(getClass()); 得到一个Logger对象,这个Logger将监视this.getClass()这个运行时类,这个运行时类里面创建了 log.info("infor")log.warn("warn")log.error("error")log.debug("debug") 语句,这些语句就会根据预先定义的Logger级别来输出你的日志。与System.out.print("")一样,但是不同的是Logger可以根据需要按显示级别进行日志输出控制。

package com.etc.aop.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Repository;
import com.etc.aop.dao.BaseDao;
import com.etc.aop.dao.UsersDao;
import com.etc.aop.entity.Users;

@Repository(value="ud")
public class UsersDaoImpl implements UsersDao {
    
    
	@Autowired
	private BaseDao bd;
	protected final Log logger = LogFactory.getLog(getClass());
	@Override
	public void addUsers(Users user) throws Exception {
    
    
		Connection conn = bd.getConn();
		System.out.println("userdao conn:"+conn);
		logger.debug("debug");
		logger.info("info");
		logger.warn("warn");
		logger.error("error");
		PreparedStatement pstmt = conn.prepareStatement("INSERT INTO `tuser` (`uname`, `sno`, `pwd`, `role`) VALUES (?,?,?,?);");
		pstmt.setString(1, user.getUname());
		pstmt.setString(2, user.getSno());
		pstmt.setString(3, user.getPwd());
		pstmt.setInt(4, user.getRole());
		pstmt.executeUpdate();
		pstmt.close();
	}
}


在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42141141/article/details/113359981