1. 程式人生 > >如何將對資料庫兩個表的操作處於用一個事物下?同一個連線物件+事物攔截

如何將對資料庫兩個表的操作處於用一個事物下?同一個連線物件+事物攔截

需求
我要儲存同時儲存一個學生資訊和這個學生購買的圖書資訊,當學生的資訊儲存失敗了,圖書資訊也不儲存了,反之也一樣,當某本書的資訊儲存失敗了,學生資訊也不儲存了。

正常情況下,如果兩個表的資訊分開儲存程式碼如下
這裡寫圖片描述
這裡寫圖片描述

所以我們可以看到兩個表是否儲存成功的資訊沒有辦法傳遞,圖書儲存情況不知道學生資訊儲存情況。

解決方法: 兩個儲存用同一個Connection物件。同時在Serivce事物層完成。
第一個問題:如何控制讓兩個表拿的是同一個Connection物件。
資料庫連線池來實現:

ThreadLocal<Connection>

ThreadLocal 是一個HashMap,Key是當前執行緒,value是Object. 因此只要是同一個執行緒在存學生資訊和圖書資訊,就可以拿到同一個連線物件。
貼上資料庫連線詞程式碼

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager; import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; import java.util.ArrayList;
import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; //////該版本相比第4版引入了ThreadLocal執行緒管理物件,以實現同一執行緒獲得的con物件是同一個/////////// public class Conn5Utils { //宣告一個單例的池 private static List<Connection> pool = new ArrayList<Connection>(); private static final int MAX=3; //執行緒管理物件 private static ThreadLocal<Connection> t = new ThreadLocal<Connection>(); static{ try { Properties p = new Properties(); p.load( Conn5Utils.class.getClassLoader().getResourceAsStream("jdbc.properties")); String driver = p.getProperty("driver"); String url = p.getProperty("url"); String user = p.getProperty("username"); String pwd = p.getProperty("password"); Class.forName(driver); for (int i = 0; i < MAX; i++) { final Connection conn = DriverManager.getConnection(url, user, pwd); //使用動態代理,實現對con.close()方法的攔截 Object proxiedObj = Proxy.newProxyInstance( Conn5Utils.class.getClassLoader(), new Class[]{Connection.class},//注意,jdbc包中con的類載入器應該不是AppClassLoader,所以此處採用“conn.getClass().getInterfaces()”方式不行 //上面一個引數,用將要把代理後物件強轉的介面型別,是最保險的! new InvocationHandler() { @Override //proxy引數就是代理後的物件,如果在invoke()方法中使用代理後物件就用這個,如果在外面就用proxiedObj public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("close")){//指定只攔截close()方法 System.out.println("往池中還回來一個連線...."); pool.add((Connection) proxy); t.set(null);//清空t池中的本地執行緒物件 return null; }else{//其它方法,放行! return method.invoke(conn, args); } } }); pool.add((Connection) proxiedObj); //必須使用代理後的物件,才有攔截功能 } } catch (Exception e) { e.printStackTrace(); } } private Conn5Utils(){ } public static synchronized Connection getConn() throws Exception{ //直接先從執行緒管理池t中去拿,若有就拿出來,若沒有就重新從pool中獲取一個並放入t中 Connection con = t.get(); if(con==null){ if(pool.size()<=0){ System.out.println("池中暫時沒了,請稍後..."); Thread.sleep(1000); return getConn(); }else{ con= pool.remove(0); t.set(con); } } return con; } }

同時把事物處理放到service層來了
這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

同時我們發現一個新的問題。。。這個Service層的Save寫的太複雜。—通過攔截代理來完成,

package cn.hncu.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

public class TxProxy implements InvocationHandler {
    private Object srcObject;

    private TxProxy(Object srcObject) {
        this.srcObject = srcObject;
    }

    /*
     * public static Object getProxy(Object srcObject){ Object proxiedObj =
     * Proxy.newProxyInstance( TxProxy.class.getClassLoader(),
     * srcObject.getClass().getInterfaces(), new TxProxy(srcObject));
     * 
     * return proxiedObj; }
     */

    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T srcObject) {
        Object proxiedObj = Proxy.newProxyInstance(TxProxy.class
                .getClassLoader(), srcObject.getClass().getInterfaces(),
                new TxProxy(srcObject));

        return (T) proxiedObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // 有@Transaction就進行攔截(進行事務處理),否則直接放行

        if (method.isAnnotationPresent(Transaction.class)) {
            Connection con = null;
            try {
                con = Conn5Utils.getConn();
                con.setAutoCommit(false);
                System.out.println("事務開啟了...");

                Object res = method.invoke(srcObject, args); // 業務程式碼//放行

                con.commit();
                System.out.println("事務提交了....");

                return res;
            } catch (Exception e) {
                try {
                    con.rollback();
                    System.out.println("事務回滾了...");
                } catch (SQLException e1) {
                    throw new RuntimeException("資料庫回滾失敗!", e1);
                }
            } finally {
                try {
                    con.setAutoCommit(true);
                    con.close();
                } catch (SQLException e) {
                    throw new RuntimeException("資料庫關閉失敗!", e);
                }
            }

            return null;
        } else {
            return method.invoke(srcObject, args);
        }

    }
}

加了事物攔截之後 Service層程式碼
這裡寫圖片描述

本來想貼原始碼的 太多了。整個專案的原始碼。想要的私信我昊了

相關推薦

如何資料庫操作處於一個事物下?同一個連線物件+事物攔截

需求: 我要儲存同時儲存一個學生資訊和這個學生購買的圖書資訊,當學生的資訊儲存失敗了,圖書資訊也不儲存了,反之也一樣,當某本書的資訊儲存失敗了,學生資訊也不儲存了。 正常情況下,如果兩個表的資訊分開儲存程式碼如下 所以我們可以看到兩個表是否儲存成功

資料庫技巧》資料庫求笛卡爾積(階乘)

最近遇到了一個需求:使用者在客戶端頁面上進行資料錄入,下拉列表的選擇,然後使用者對頁面資料進行提交。後臺要根據客戶端傳來的資料進行分析,並且生成一串數字,將該數字串進行儲存。 介紹之前,我們要了解本文的一個名詞【笛卡爾積】,同俗的來講,就是數學中的排列組合。

中查出的列信息放在同一個

i++ trade ear .cn select 一行 gdi record 企業 String sql_gd = "select * from TAX_INFO_GD where ID=‘"+gdid+"‘"; Record gdRecord = Db

2.5給定表示的整數,每個結點包含一個數位。這些數位是反向存放的,也就是個位排在鏈首部。編寫函數整數求和,並用鏈形式返回結果。

直接 logs next 末尾 做的 nbsp before != 結果 其實仔細想想是挺簡單的,我們要做的只是記得進位。 LinkedListNode addLists(LinkedListNode l1, LinkedListNode l2, int carry) /

遞增的有序連結串列合併為一個遞增的有序連結串列。要求結果連結串列扔使用原來連結串列的儲存空間,不另外佔用其他的儲存空間。中不允許有重複的資料。

語言:C++ #include <iostream> using namespace std; typedef struct LNode { int data; LNode *next; }LNode,*LinkList; //建立連結串列 int CreateList(Li

JDBC上關於資料庫中多操作一對多關係和多多關係的實現方法--轉

  原文地址---- https://www.cnblogs.com/pangguoming/p/7028322.html 黑馬程式設計師 我們知道,在設計一個Java bean的時候,要把這些BEAN 的資料存放在資料庫中的表結構,然而這些資料庫中的表直接又有些特殊

JDBC事務(同時操作資料庫)

public class TestTransaction {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubtry {Class.forN

如何把區域網內不同資料庫的資料進行傳輸?

應用場景:當測試資料庫的資料不小心被清空了,需要從別的庫裡把資料恢復過來;或者測試庫增加了某表的一些資料,正式庫需要同時更新(當然穩妥的是儲存更新語句)等等,這時就需要用到這個小技巧了。 第一句是把b表中的選單表的資料放到當前資料庫中,並且新建一張tmenu表: select * into TMENU f

MyBatis整合多的類的操作

前言 前幾天在實現oj的DAO層時,由於將problem表中的一些欄位拿出來做了字典表,導致了資料庫表過多,如果還是像以前一樣: 一個數據庫表對應一個實體類的話,這樣不僅會增加好多重複性的工作,還會使

序列合併為一個有序列表

class Solution(object): def merge(self, nums1, m, nums2, n): """ Select number of m elements from nums1 and n

Linq_根據條件查詢,並返回不同型別的結果合併

var deliveryOrderDetail = from d in _context.DeliveryOrderDetails

String型別集合的比(或 的比

equBaseInfoSubList為主要基準,    openStatusList比它多的要刪除,少的要新增。 自己在專案中遇到的,兩個表的比對。 主要思想:                 數學中的交集思想:A,B兩個集合。先找出A,B的交集,A-交集=需要新增的部分;

查詢不同sqlserver資料庫並比欄位結構是否相同

package com.cn.sis;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;im

NNER JOIN連接、三、五的SQL語句

from span 至少 一個 color pre identity bsp 語句 NNER JOIN連接兩個表、三個表、五個表的SQL語句 2013-04-14 15:13:11來源:西部e網作者: SQL INNER JOIN關鍵字表示在表中存在至少一個匹配時,IN

laravel關聯內容取出的辦法

facades tab article port min lar aca 關聯 取出 use Illuminate\Support\Facades\DB; $articles = DB::table(‘articles‘)->join(‘category‘,‘arti

五層結構 判斷IP是否處於同一子網?

網絡管理 主機ip .cn 文件 七層 smt 無限 可靠傳輸 log 互聯網協議按照功能不同分為osi七層或tcp/ip五層或tcp/ip四層 應用層(各種協議) 端口 H

mysql 查相同的值

mysql 查 code from class style mysq bsp sel password 比如一個數據庫 表A和表B 都有一個username字段, 現查出與表A中username值相同的表B的username和password數據 select B

MongoDB-Java的基本操作Upsert和insertMany

slist 出現 兩個 我想 option ceo logs 方法 lis   此文只是為了記錄幾個基本操作,首先Upsert,有多種方法可以進行,但是都需要指定UpdateOptions.upsert(true),其中最簡單的辦法如下(eqq是一個用來filter的BSO

【PostgresSQL】同時更新

post style gre column div tab pre sql from UPDATE table1 SET column = value FROM table2 WHERE table1.column2 = table2.column2 【Po

組合

sql 類型 pre AD rip span scroll lan con 表1: Person +-------------+---------+ | 列名 | 類型 | +-------------+---------+ | P