业务层事务管理
事务管理
DAO层事务管理
dao层一般完成单个数据库操作,使用Connection可以控制单个事务的提交,回滚。
Service层事务管理
service层一般完成一定的业务逻辑,需要使用多个dao的操作组合完成。所以这些操作要么全部完成要么全部不做。也就是一个事务。
事务管理要加在哪里?
答:Service层,控制整个业务逻辑的隔离性。
例子:转账
View、Servlet和Domain代码
View层:
<h1>转账页面</h1>
<form action="${pageContext.request.contextPath }/AccountServlet" method="post">
付款人:<input type="text" name="from"><br>
收款人:<input type="text" name="to"><br>
金额:<input type="text" name="money"><br>
<input type="submit" value="提交">
</form>
</body>
Domain:
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
}
Jdbc工具类:
static ComboPooledDataSource dataSource = null;
static{
dataSource=new ComboPooledDataSource();
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
public static DataSource getDataSource(){
return dataSource;
}
public static void close(Connection con,PreparedStatement ps,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws SQLException {
System.out.println(new JDBCUtils().getConnection());
}
public static void startTransaction() {
startTransaction();
}
Servlet层:调用业务层的服务进行转账
public class AccountServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String from = request.getParameter("from");
String to = request.getParameter("to");
request.setCharacterEncoding("utf-8");//post乱码
Double money = Double.parseDouble(request.getParameter("money"));
//处理数据
AccountService service = new AccountService();
try {
service.transfer(from ,to ,money);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
事务控制第一种方法:传递Service传递一个Connection给Dao层
Service层分析:分别调用Dao层的两个方法,完成一个事务,一个事务必须使用同一个Connection设置自动提交false,在业务层完成事务的提交/回滚。
* 事务管理第一种解决办法:向下传递Connection
* @param from
* @param to
* @param money
* @throws SQLException
*/
public void transfer(String from, String to, Double money) throws SQLException {
AccountDao accountDao = new AccountDao();
//方法1:业务层获得Connection 传给dao
Connection conn = null;
try {
conn= JDBCUtils.getConnection();
conn.setAutoCommit(false);
accountDao.transferOut(from,money,conn);
int i=1/0;
accountDao.transferIn(to,money,conn);
} catch (Exception e) {
System.out.println("rollback");
conn.rollback();
e.printStackTrace();
}
conn.commit();
}
对应Dao层代码:接收Service层的Connection
* 第一种:传递Connection
* @param from
* @param money
* @param conn
* @throws SQLException
*//*
public void transferOut(String from, Double money, Connection conn) throws SQLException {
//Connection conn = null;
PreparedStatement ps=null;
try{
//conn = JDBCUtils.getConnection();
String sql = "update account set money = money-? where name =?";
ps = (PreparedStatement) conn.prepareStatement(sql);
ps.setDouble(1, money);
ps.setString(2, from);
ps.executeUpdate();
}catch (Exception e) {
e.printStackTrace();
}finally {
ps.close();
//conn.close();
}
}
public void transferIn(String to, Double money, Connection conn) throws SQLException {
//Connection conn = null;
PreparedStatement ps=null;
try{
//conn = JDBCUtils.getConnection();
String sql = "update account set money = money+? where name =?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, money);
ps.setString(2, to);
ps.executeUpdate();
}catch (Exception e) {
e.printStackTrace();
}finally {
ps.close();
//conn.close();
}
}
事务控制第二种方法:使用ThreadLocal
ThreadLocal使用Map<key,value>将一个变量与线程绑定,key是线程,value是与绑定线程的变量。将Connection作为变量与线程绑定,就保证了在执行Service时,每个线程在执行每个Dao操作时,获取同一个Connection。
Service层代码:
* 事务管理,使用ThreadLocal
* @param from
* @param to
* @param money
* @throws SQLException
*/
public void transfer2(String from, String to, Double money) throws SQLException {
AccountDao accountDao = new AccountDao();
Connection conn = null;
try {
conn= JDBCUtils2.getConnection();
JDBCUtils2.startTransaction();
accountDao.transferOut(from,money);
//int i=1/0;
accountDao.transferIn(to,money);
} catch (Exception e) {
System.out.println("rollback");
JDBCUtils2.rollBackTranaction();
e.printStackTrace();
}
JDBCUtils2.commitTransaction();
}
JDBCUtils2:保证获得的Connection是与当前线程绑定的Conn
static ComboPooledDataSource dataSource = null;
static final ThreadLocal<Connection> tl=new ThreadLocal<Connection>();
//连接与线程绑定ThreadLocal的Map里放的是<Thread,Connection>
static{
dataSource=new ComboPooledDataSource();
}
public static Connection getConnection() throws SQLException{
Connection conn = null;
if(tl.get()==null){
conn = dataSource.getConnection();
tl.set(conn); //获取线程时连接放入线程
}
return tl.get();//取出线程绑定的连接
}
public static DataSource getDataSource(){
return dataSource;
}
public static void close(Connection con,PreparedStatement ps,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void startTransaction() throws SQLException {
Connection conn = null;
conn = tl.get();
if(conn==null){
conn=dataSource.getConnection();
tl.set(conn);
}
conn.setAutoCommit(false);//线程绑定的连接设置不自动提交
}
public static void commitTransaction() throws SQLException{
Connection connection = tl.get();
connection.commit(); ///线程绑定的连接提交事务
}
public static void rollBackTranaction() throws SQLException{
Connection connection = tl.get();
connection.rollback();//线程绑定的连接回滚
}
}
Dao层代码:获得的链接是与当前线程绑定的Connection
public void transferOut(String from, Double money) throws SQLException {
Connection conn = null;
PreparedStatement ps=null;
try{
conn = JDBCUtils2.getConnection();
String sql = "update account set money = money-? where name =?";
ps = (PreparedStatement) conn.prepareStatement(sql);
ps.setDouble(1, money);
ps.setString(2, from);
ps.executeUpdate();
}catch (Exception e) {
e.printStackTrace();
}finally {
ps.close();
//conn.close();
}
}
public void transferIn(String to, Double money) throws SQLException {
/*//没有事务管理dbutils时这样用
* QueryRunner query=new QueryRunner(JDBCUtils.getDataSource());
String sql = "update account set money = money+? where name =?";
query.update(sql,money,to);
*/
Connection conn = null;
PreparedStatement ps=null;
try{
conn = JDBCUtils2.getConnection();
String sql = "update account set money = money+? where name =?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, money);
ps.setString(2, to);
ps.executeUpdate();
}catch (Exception e) {
e.printStackTrace();
}finally {
ps.close();
//conn.close();
}
}
使用DBUtils的事务控制:传递相同Connection
Service层:传递普通Connection给dao层
* 事务管理,使用Dbutils和第一种类似,都是传递Connection
* @param from
* @param to
* @param money
* @throws SQLException
*/
public void transfer(String from, String to, Double money) throws SQLException {
AccountDao accountDao = new AccountDao();
Connection conn = null;
try {
conn= JDBCUtils.getConnection();
conn.setAutoCommit(false);
accountDao.transferOut(from,money,conn);
int i=1/0;
accountDao.transferIn(to,money,conn);
} catch (Exception e) {
System.out.println("rollback");
DbUtils.rollbackAndClose(conn);
e.printStackTrace();
}
DbUtils.commitAndCloseQuietly(conn);
}
dao层使用DBUtils进行数据库操作:
* 第一种:DButils
* @param from
* @param money
* @param conn
* @throws SQLException
*/
public void transferOut(String from, Double money, Connection conn) throws SQLException {
//有事务管理dbutils时这样用
QueryRunner query=new QueryRunner();
String sql = "update account set money = money-? where name =?";
//传Connection
query.update(conn,sql,money,from);
}
public void transferIn(String to, Double money, Connection conn) throws SQLException {
//有事务管理dbutils时这样用
QueryRunner query=new QueryRunner();
String sql = "update account set money = money+? where name =?";
//传Connection
query.update(conn,sql,money,to);
}
Hibernate事务管理:ThreadLocal
Hibernate使用ThreadLocal管理事务,程序员不必编写代码进行控制,只需要在核心配置文件里配置即可设置对应的事务管理级别
<property name="hibernate.current_session_context_class">thread</property>
Hibernate工具类里通过SessionFactory的getCurrentSession()获得与线程绑定的Connection,不再配置文件里配置是使用不了这个方法的。
public static Session getCurrentSeesion(){
//xml配置以后才可以使用
return factory.getCurrentSession();
}
Hibernate使用:
Session session = HibernateUtils.getCurrentSeesion();
Transaction transaction = session.beginTransaction();
Customer c= new Customer();
c.setCust_name("王");
session.save(c);
transaction.commit();
//不要关闭session,线程结束会自动关闭