如何將對資料庫兩個表的操作處於用一個事物下?同一個連線物件+事物攔截
需求:
我要儲存同時儲存一個學生資訊和這個學生購買的圖書資訊,當學生的資訊儲存失敗了,圖書資訊也不儲存了,反之也一樣,當某本書的資訊儲存失敗了,學生資訊也不儲存了。
正常情況下,如果兩個表的資訊分開儲存程式碼如下
所以我們可以看到兩個表是否儲存成功的資訊沒有辦法傳遞,圖書儲存情況不知道學生資訊儲存情況。
解決方法: 兩個儲存用同一個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