为什么加个注解@Transtaional就可以保证事务的一致性和完整性?

<?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 http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<context:component-scan base-package="com.atguigu.book_xml"></context:component-scan>
	
	<!-- 引入属性文件 -->
	<context:property-placeholder location="db.properties"/>

	<!-- 创建数据源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driver}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>

	<!-- 通过数据源配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务管理器,不管是用注解方式或xml方式配置事务,一定要有DataSourceTransactionManager事务管理器的支持 -->
	<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务通知 -->
	<tx:advice id="tx" transaction-manager="dataSourceTransactionManager">
		<tx:attributes>
			<!-- 在设置好的切入点表达式下再次进行事务设置 -->
			<tx:method name="buyBook"/>
			<tx:method name="checkOut"/>
			
			<!-- 只有select开头的方法才会被事务处理 -->
			<tx:method name="select*" read-only="true"/>
			<tx:method name="insert*"/>
			<tx:method name="update*"/>
			<tx:method name="delete*"/>
			
			<tx:method name="*"/>
			
		</tx:attributes>
	</tx:advice>

	<!-- 配置切入点表达式 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.atguigu.book_xml.service.impl.*.*(..))" id="pointCut"/>
		<aop:advisor advice-ref="tx" pointcut-ref="pointCut"/>
	</aop:config>
	
</beans>

在xml里面定义了数据源bean,数据源bean又被事务管理器bean锁依赖,这时候用的数据源连接对象connection就是同一个,spring管理对象默认为单例

package com.liuyuanayuan.book.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import com.liuyuanyuan.book.service.BookService;
import com.liuyuanyuan.book.service.Cashier;

@Controller
public class BookController {

	@Autowired
	private BookService service;
	
	@Autowired
	private Cashier cashier;
	
	public void buyBook() {
		service.buyBook("1", "1001");
	}
	
	public void checkOut() {
		List<String> bids = new ArrayList<>();
		bids.add("1");
		bids.add("2");
		cashier.checkOut("1001", bids);
	}
	
}
package com.liuyuanyuan.book.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.liuyuanyuan.book.service.BookService;
import com.liuyuanyuan.book.service.Cashier;

@Service
@Transactional
public class CashierServiceImpl implements Cashier {

	@Autowired
	private BookService service;
	
	@Override
	public void checkOut(String uid, List<String> bids) {
		for (String bid : bids) {
			service.buyBook(bid, uid);
		}
	}

}
package com.liuyuanyuan.book.service.impl;

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 com.liuyuanyuan.book.dao.BookDao;
import com.liuyuanyuan.book.exception.MyException;
import com.liuyuanyuan.book.service.BookService;

@Service
//@Transactional
public class BookServiceImpl implements BookService {

	@Autowired
	private BookDao dao;
	
	/**
	 * @Transactional:对方法中所有的操作作为一个事务进行管理
	 * 在方法上使用,只对方法有效果
	 * 在类上使用,对类中所有的方法都有效果
	 * @Transactional中可以设置的属性:
	 * 
	 * propagation:A方法和B方法都有事务,当A在调用B时,会将A中的事务传播给B方法,B方法对于事务的处理方式就是事务的传播行为
	 * Propagation.REQUIRED:必须使用调用者的事务
	 * Propagation.REQUIRES_NEW:将调用者的事务挂起,不使用调用者的事务,使用新的事务进行处理
	 * 
	 * isolation:事务的隔离级别,在并发的情况下,操作数据的一种规定
	 * 			读未提交:脏读   1
	 * 			读已提交:不可重复读   2
	 * 			可重复读:幻读   4
	 * 			串行化:性能低,消耗大   8
	 * 
	 * 
	 * timeout:在事务强制回滚前最多可以执行(等待)的时间
	 * 
	 * readOnly:指定当前事务中的一系列的操作是否为只读
	 * 若设置为只读,不管事务中有没有写的操作,mysql都会在请求访问数据的时候,不加锁,提高性能
	 * 但是如果有写操作的情况,建议一定不能设置只读
	 * 
	 * rollbackFor|rollbackForClassName|noRollbackFor|noRollbackForClassName:设置事务回滚的条件
	 */
	
	@Transactional(propagation=Propagation.REQUIRES_NEW, timeout=3, noRollbackFor= {NullPointerException.class, MyException.class})
	public void buyBook(String bid, String uid) {
		/*try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		Integer price = dao.selectPrice(bid);
		dao.updateSt(bid);
		dao.updateBalance(uid, price);
	}
}
package com.liuyuanyuan.book.dao.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.liuyuanyuan.book.dao.BookDao;
import com.liuyuanyuan.book.exception.MyException;

@Repository
public class BookDaoImpl implements BookDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Override
	public Integer selectPrice(String bid) {
		Integer price = jdbcTemplate.queryForObject("select price from book where bid = ?", new Object[] {bid}, Integer.class);
		return price;
	}

	@Override
	public void updateSt(String bid) {
		//获取该书籍的库存
		Integer st = jdbcTemplate.queryForObject("select st from stock where sid = ?", new Object[] {bid}, Integer.class);
		if(st <= 0) {
			throw new RuntimeException();
		}else {
			jdbcTemplate.update("update stock set st = st -1 where sid = ?", bid);
		}
		
	}

	@Override
	public void updateBalance(String uid, Integer price) {
		Integer balance = jdbcTemplate.queryForObject("select balance from money where uid = ?", new Object[] {uid}, Integer.class);
		if(balance < price) {
			throw new MyException("余额不足");
		}else {
			jdbcTemplate.update("update money set balance = balance - ? where uid = ?", price, uid);
		}
	}
	
	
	
}

当然我也可以定义一个ThreadLocal<Connection> connection 

package com.liuyuanyuan.util;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
 * 获取连接和释放连接的工具类
 * @author yunyuan liu
 *
 */
public class JDBCUtils {
	private static DataSource dataSource;
	
	/**
	 * ThreadLocal
	 * 		get()
	 * 		set()
	 *      remove()
	 */
	private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
	
	static {
		try {
			//1、读取druip.properties文件
			Properties pro = new Properties();
			pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
			
			//2、连接连接池
			dataSource = DruidDataSourceFactory.createDataSource(pro);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//获取连接
	public static Connection getConnection() {
		Connection connection = threadLocal.get();
		try {
			if(connection == null) {
				connection = dataSource.getConnection();
				threadLocal.set(connection);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}
//	public static Connection getConnection() {
//		Connection connection = null;
//		try {
//			connection = dataSource.getConnection();
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		return connection;
//	}

	//释放连接
	public static void releaseConnection() {
		Connection connection = threadLocal.get();
		if(connection != null) {
			try {
				connection.close();
				threadLocal.remove();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
//	public static void releaseConnection(Connection connection) {
//		if(connection != null) {
//			try {
//				connection.close();
//			} catch (SQLException e) {
//				e.printStackTrace();
//			}
//		}
//	}
}
package com.liuyuanyuan.filter;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.liuyuanyuan.util.JDBCUtils;

/**
 * Servlet Filter implementation class TransactionFilter
 */
public class TransactionFilter extends HttpFilter {

	/**
	 * 1. 处理异常
	 * 2. 统一处理事务
	 */
	@Override
	public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		Connection connection = JDBCUtils.getConnection();
		try {
			//开启事务
			connection.setAutoCommit(false);
			//放行
			chain.doFilter(request, response);
			//无异常,提交事务
			connection.commit();
		} catch (Exception e) {
			//有异常,回滚事务
			try {
				connection.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
			response.sendRedirect(request.getContextPath()+"/pages/error/transaction_error.jsp");
		}finally {
			//释放Connection
			JDBCUtils.releaseConnection();
		}
		
	}
       
   

}
package com.liuyuanyuan.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.liuyuanyuan.bean.Cart;
import com.liuyuanyuan.bean.User;
import com.liuyuanyuan.service.OrderService;
import com.liuyuanyuan.service.impl.OrderServiceImpl;

/**
 * Servlet implementation class OrderServlet
 */
public class OrderServlet extends BaseServlet {
	private static final long serialVersionUID = 1L;
       
	private OrderService orderService = new OrderServiceImpl();
	
	protected void checkout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//取值
		HttpSession session = request.getSession();
		Cart cart = (Cart)session.getAttribute("cart");
		User user  = (User)session.getAttribute("user");
		//调用service
		String orderId = orderService.createOrder(cart, user);
		session.setAttribute("orderId", orderId);
		//跳转
		response.sendRedirect(request.getContextPath()+"/pages/cart/checkout.jsp");
	}

}
package com.liuyuanyuan.service.impl;

import java.util.Date;
import java.util.List;

import com.liuyuanyuan.bean.Book;
import com.liuyuanyuan.bean.Cart;
import com.liuyuanyuan.bean.CartItem;
import com.liuyuanyuan.bean.Order;
import com.liuyuanyuan.bean.OrderItem;
import com.liuyuanyuan.bean.User;
import com.liuyuanyuan.dao.BookDao;
import com.liuyuanyuan.dao.OrderDao;
import com.liuyuanyuan.dao.OrderItemDao;
import com.liuyuanyuan.dao.impl.BookDaoImpl;
import com.liuyuanyuan.dao.impl.OrderDaoImpl;
import com.liuyuanyuan.dao.impl.OrderItemDaoImpl;
import com.liuyuanyuan.service.OrderService;
import com.liuyuanyuan.util.JDBCUtils;

public class OrderServiceImpl implements OrderService {

	private OrderDao orderDao = new OrderDaoImpl();
	private OrderItemDao orderItemDao = new OrderItemDaoImpl();
	private BookDao bookDao = new BookDaoImpl();
	/**
	 * * 去结账createOrder
	 * 		1. 生成订单
	 * 		2. 生成订单详情
	 * 		3. 更改相应book的库存和销量
	 * 		4. 清空购物车
	 * 
	 * * 批处理优化结账
	 * 		 * 1. BaseDao:添加batchUpdate()
	 * 			* queryRunner.batch(connection, sql, params);
	 * 			* params:Object[][]
	 * 			* 一维:次数
	 * 			* 二维:参数
			 * 2. OrderItemDao添加批处理接口
			 * 3. BookDao添加批处理接口
			 * 4. OrderServiceImpl调用dao批处理接口
			 * 		* Object[][] orderItemParams = new Object[cartItems.size()][];
					  Object[][] bookParams = new Object[cartItems.size()][];
					* orderItemParams[i] = new Object[]{参数的顺序};
	    * 使用事务优化结账
		 	* 开启事务:connection.setAutoCommit(false);|commit() rollback()
		 		1. 共用同一个connetion
		 			* ThreadLocal管理Connection
		 			* 删除BaseDao中的JDBCUtils.releaseConnection(connection);
		 		2. 统一处理异常(Filter)
		 			* 抛出BaseDao和BaseServlet中的异常,统一在Filter中处理
		 			* 统一开启事务,提交|回滚事务。
		
	 */
	@Override
	public String createOrder(Cart cart, User user) {
		//1. 生成订单
		//orderId=时间戳+userid
		String orderId = System.currentTimeMillis()+""+user.getId();		//null.
		orderDao.insertOrder(new Order(orderId, new Date(), cart.getTotalCount(),
				cart.getTotalAmount(), 0, user.getId()));
		
		//获取所有购物项
		List<CartItem> cartItems = cart.getCartItems();
		//OrderItem的二维参数
		Object[][] orderItemParams = new Object[cartItems.size()][];
		Object[][] bookParams = new Object[cartItems.size()][];
		
		//遍历购物项,添加到订单详情
		for (int i=0;i<cartItems.size();i++) {
			//2. 生成订单详情
			CartItem cartItem = cartItems.get(i);
			Book book = cartItem.getBook();
			int count = cartItem.getCount();
//			orderItemDao.insertOrderItem(new OrderItem(null, count,
//					cartItem.getAmount(), book.getTitle(), book.getAuthor(),
//					book.getPrice(), book.getImgPath(), orderId));
			//orderItemParams第二维赋值
			//`count`,amount,title,author,price,img_path,order_id
			orderItemParams[i] = new Object[] {count,
					cartItem.getAmount(), book.getTitle(), book.getAuthor(),
					book.getPrice(), book.getImgPath(), orderId};
			//3. 更改相应book的库存和销量
			int stock = book.getStock()-count;	//计算最终的库存
			int sales = book.getSales()+count;  //计算最终的销量
			//sales=?,stock=? where id=?
			bookParams[i] = new Object[] {sales,stock,book.getId()};
//			bookDao.updateBook(stock, sales, book.getId());
		}
		
		orderItemDao.insertOrderItem(orderItemParams);
		bookDao.updateBook(bookParams);
		
		//4. 清空购物车
		cart.emptyCart();
		return orderId;
	}

}
package com.liuyuanyuan.dao.impl;

import com.liuyuanyuan.bean.OrderItem;
import com.liuyuanyuan.dao.BaseDao;
import com.liuyuanyuan.dao.OrderItemDao;

public class OrderItemDaoImpl extends BaseDao<OrderItem> implements OrderItemDao {

	@Override
	public void insertOrderItem(OrderItem orderItem) {
		String sql = "INSERT INTO order_item(`count`,amount,title,author,price,img_path,order_id) VALUES(?,?,?,?,?,?,?)";
		this.update(sql, orderItem.getCount(),orderItem.getAmount(),orderItem.getTitle(),orderItem.getAuthor(),orderItem.getPrice(),orderItem.getImgPath(),orderItem.getOrderId());
	}

	/**
	 * 1. BaseDao:添加batchUpdate()
	 * 		* queryRunner.batch(connection, sql, params);
	 * 		* params:Object[][]
	 * 			* 一维:次数
	 * 			* 二维:参数
	 * 2. OrderItemDao添加批处理接口
	 * 3. BookDao添加批处理接口
	 * 4. OrderServiceImpl调用dao批处理接口
	 * 		* Object[][] orderItemParams = new Object[cartItems.size()][];
			  Object[][] bookParams = new Object[cartItems.size()][];
			* orderItemParams[i] = new Object[]{参数的顺序};
	 * 		
	 */
	@Override
	public void insertOrderItem(Object[][] params) {
		String sql = "INSERT INTO order_item(`count`,amount,title,author,price,img_path,order_id) VALUES(?,?,?,?,?,?,?)";
		this.batchUpdate(sql, params);
	}

}
package com.liuyuanyuan.dao.impl;

import java.util.List;

import com.liuyuanyuan.bean.Book;
import com.liuyuanyuan.bean.Page;
import com.liuyuanyuan.dao.BaseDao;
import com.liuyuanyuan.dao.BookDao;

public class BookDaoImpl extends BaseDao<Book> implements BookDao {

	@Override
	public List<Book> getAllBooks() {
		String sql = "SELECT id,title,author,price,sales,stock,img_path FROM books";
		return this.getBeanList(sql);
	}

	@Override
	public void addBook(Book book) {
		String sql = "insert into books(title,author,price,sales,stock,img_path) values(?,?,?,?,?,?)";
		this.update(sql, book.getTitle(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());
	}

	@Override
	public void delBookById(String id) {
		String sql = "delete from books where id = ?";
		this.update(sql, id);
	}

	@Override
	public Book getBookById(String id) {
		String sql = "SELECT id,title,author,price,sales,stock,img_path FROM books WHERE id = ?";
		return this.getBean(sql, id);
	}

	@Override
	public void updateBook(Book book) {
		String sql = "update books set title=?,author=?,price=?,sales=?,stock=? where id=?";
		this.update(sql, book.getTitle(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getId());
	}
	@Override
	public void updateBook(int stock, int sales, int id) {
		String sql = "update books set sales=?,stock=? where id=?";
		this.update(sql,sales,stock,id);
	}
	@Override
	public void updateBook(Object[][] params) {
		String sql = "update books set sales=?,stock=? where id=?";
		this.batchUpdate(sql,params);
	}
	@Override
	public Page<Book> getBooksByPage(Page<Book> page) {
		//page:pageNo PAGE_SIZE totalPageNo
		//dao:totalRecord
		String sql = "select count(*) from books";
		int count = Integer.parseInt(this.getSingeValue(sql)+"");  //long(int) Integer.parse
		//将totalRecord赋值
		page.setTotalRecord(count);
//		System.out.println("record:"+page.getTotalRecord());
//		System.out.println("pageNo:"+page.getTotalPageNo());
		//dao:list
		String sql2 = "SELECT id,title,author,price,sales,stock,img_path "
				+ "FROM books "
				+ "WHERE 1=1 "
				+ "LIMIT ?,?";
		List<Book> list = this.getBeanList(sql2, (page.getPageNo()-1)*Page.PAGE_SIZE,Page.PAGE_SIZE);
		//将list存放到page中
		page.setList(list);
		return page;
	}

	@Override
	public Page<Book> getBooksByPageAndPrice(Page<Book> page,double min,double max) {
		String sql = "SELECT count(*) "
				+ "FROM books "
				+ "WHERE 1=1 "
				+ "AND price BETWEEN ? AND ?";
		int count = Integer.parseInt(this.getSingeValue(sql, min,max)+"");  //long(int) Integer.parse
		//将totalRecord赋值
		page.setTotalRecord(count);
		//dao:list
		String sql2 = "SELECT id,title,author,price,sales,stock,img_path "
				+ "FROM books "
				+ "WHERE 1=1 "
				+ "AND price BETWEEN ? AND ? "
				+ "LIMIT ?,?";
		List<Book> list = this.getBeanList(sql2, min,max,(page.getPageNo()-1)*Page.PAGE_SIZE,Page.PAGE_SIZE);
		//将list存放到page中
		page.setList(list);
		return page;
	}


	
	
}
package com.liuyuanyuan.dao.impl;

import com.liuyuanyuan.bean.Order;
import com.liuyuanyuan.dao.BaseDao;
import com.liuyuanyuan.dao.OrderDao;

public class OrderDaoImpl extends BaseDao<Order> implements OrderDao {

	@Override
	public void insertOrder(Order order) {
		String sql = "INSERT INTO orders(id,order_time,total_count,total_amount,state,user_id) VALUES(?,?,?,?,?,?)";
		this.update(sql, order.getId(),order.getOrderTime(),order.getTotalCount(),order.getTotalAmount(),order.getState(),order.getUserId());
	}

}

 

转载于:https://my.oschina.net/architectliuyuanyuan/blog/3062567

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值