<?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());
}
}