开源框架DBUtil的使用以及使用Threadlocal类控制事务案例
2015-09-16 20:26
330 查看
一、 会编写自定义的JDBC框架核心(DBAssist)
并了解策略设计模式
二、开源的DBUtil框架的使用
2.1 需要的jar包:
commons-dbutils-1.4.jar
commons-dbcp-1.4.jar (使用DBCP数据源或C3P0数据源)
commons-pool-1.5.6.jar
mysql-connector-java-5.0.8-bin.jar (MySQL数据库驱动)
2.2 配置文件
DBCP所需要的配置文件: dbcpconfig.properties
----------------------------------------------------------------------------------------
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day16
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ
----------------------------------------------------------------------------------------
2.3 工具类
DBCP所需要的工具类DBCPUtil
----------------------------------------------------------------------------------------
public class DBCPUtil {
private static DataSource ds;
static{
try {
InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(in);
ds = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataSource getDataSource(){
return ds;
}
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
----------------------------------------------------------------------------------------
2.4 DBUtil开源框架中QueryRunner的基本使用例子以及熟悉各种结果处理器的使用
-----------------------------------------------------------------------------------------
所需要的数据库:
create database day17;
use day17;
create table t1(
id int primary key,
name varchar(100),
birthday date
);
create table t2(
id int primary key,
content longtext
);
create table t3(
id int primary key,
content longblob
);
-----------------------------------------------------------------------------------------
// 例1: 完成增删改和批处理
public class DbUtilDemo1 {
private QueryRunner qr = new QueryRunner(DBCPUtil.getDataSource());
@Test
public void testAdd1() throws Exception{
// 注: 后面两个为可变参数,也可以用一个数组扩起来
qr.update("insert into t1 (id,name,birthday) values(?,?,?)", 1,"gfy",new Date());
}
@Test // 注: mysql中可以直接输入yyyy-MM-dd形式的日期格式
public void testAdd2() throws Exception{
qr.update("insert into t1 (id,name,birthday) values(?,?,?)", 2,"zqy","1994-02-23");
}
@Test
public void testUpdate() throws Exception{
qr.update("update t1 set birthday=? where name=?", "1990-08-08","zqy");
}
@Test
public void testDel() throws Exception{
qr.update("delete from t1 where id=?",1);
}
@Test // 批处理 向表中插入10条记录
public void testBatch() throws Exception{
// 二维数组: 第一位:记录的条数; 第二维:每条记录中需要的参数
Object params[][] = new Object[10][];
for(int i=0;i<10;i++){
params[i] = new Object[]{i+1,"aaa"+(i+1),new Date()};
}
qr.batch("insert into t1 (id,name,birthday) values(?,?,?)", params);
}
@Test // 存储大文本 注: 不建议使用,对于大的文本,内存可能溢出
public void testClob() throws Exception{
File file = new File("src/jpm.txt");
Reader r = new FileReader(file);
char[] ch = new char[(int) file.length()]; // 文本的长度
// 将文本写到数组中,记得关流
r.read(ch);
r.close();
Clob clob = new SerialClob(ch);
qr.update("insert into t2 (id,content) values(?,?)",1,clob);
}
@Test // 存储图片 注: 验证: select count(*) from t3; 千万不要用查询*来验证 好奇心害死猫
public void testBlob() throws Exception{
InputStream in = new FileInputStream("src/0.jpg");
byte[] b = new byte[in.available()];
in.read(b);
in.close();
Blob blob = new SerialBlob(b);
qr.update("insert into t3 (id,content) values(?,?)",1,blob);
}
}
-----------------------------------------------------------------------------------------
// 例2: 完成查询操作(注: javaBean中的Account类,字段有id,name,money)
并熟悉所有的结果处理器,若有其他需要,则可以自己实现处理器
public class DbUtilDemo2 {
private QueryRunner qr = new QueryRunner(DBCPUtil.getDataSource());
@Test // BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中
// 查询结果集中的记录 注: 只适合结果集中有一条记录的情况,若有多条记录,则只返回第一条
public void test1() throws SQLException{
Account a = qr.query("select * from account where id=?", new BeanHandler<Account>(Account.class), 1);
System.out.println(a);
}
@Test // BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中
// 查询结果集中的多条记录
public void test2() throws SQLException{
List<Account> list = qr.query("select * from account", new BeanListHandler<Account>(Account.class));
for(Account a:list){
System.out.println(a);
}
}
@Test // ArrayHandler: 把结果集中的第一行数据转换成对象数组,只适合结果集只有一条记录的情况
public void test3() throws SQLException{
// 该数组中每个元素就是记录的每列的值
Object[] objs = qr.query("select * from account where id=?", new ArrayHandler(), 1);
for(Object o : objs){
System.out.println(o);
}
}
@Test // ArrayListHandler: 把结果集中的第一行数据转换成对象数组,适合结果集中有多条记录的情况
public void test4() throws SQLException{
List<Object[]> list = qr.query("select * from account", new ArrayListHandler());
for(Object[] objs : list){
System.out.println("--------");
for(Object o : objs){
System.out.println(o);
}
}
}
@Test // ColumnListHandler: 将结果集中的某一列的数据存放到list中
public void test5() throws SQLException{
List<Object> list = qr.query("select * from account", new ColumnListHandler("name"));
for(Object o : list){
System.out.println(o);
}
}
@Test // MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
public void test6() throws SQLException{
Map<String,Object> map = qr.query("select * from account where id=?", new MapHandler(),1);
for(Map.Entry<String,Object> me : map.entrySet()){
System.out.println(me.getKey()+"="+me.getValue());
}
}
@Test // KeyedHandler(name):内部是一个双层Map 注: 要学会该双重Map集合的遍历
// 将结果集中的每一行数据都封装到一个Map<列名,列值>里,再将这些map存到一个Map中,其key为指定的key
public void test7() throws SQLException{
Map<Object,Map<String,Object>> bmap = qr.query("select * from account", new KeyedHandler("id"));
for(Map.Entry<Object, Map<String,Object>> bme : bmap.entrySet()){
System.out.println("----------");
Map<String,Object> lmap = bme.getValue();
for(Map.Entry<String, Object> lme : lmap.entrySet()){
System.out.println(lme.getKey()+"="+lme.getValue());
}
}
}
@Test // MapListHandler: 将结果集中的每一行数据都封装到一个map里,然后再存放到list
public void test8() throws SQLException{
List<Map<String,Object>> list = qr.query("select * from account", new MapListHandler());
for(Map<String,Object> map:list){
System.out.println("---------");
for(Map.Entry<String,Object> me: map.entrySet()){
System.out.println(me.getKey()+"="+me.getValue());
}
}
}
@Test // ScalarHander: 适合取一条一列的记录,比如记录总数
public void test9() throws SQLException{
Object obj = qr.query("select count(*) from account", new ScalarHandler(1));
System.out.println(obj.getClass().getName()); // 注: obj为long类型的
int num = ((Long)obj).intValue();
System.out.println(num);
}
}
-----------------------------------------------------------------------------------------
三、ThreadLocal类
3.1、 线程局部变量类ThreadLocal: 内部有一个Map, key为当前线程对象,value:为你放入的东东
特点: 只能本线程获取本线程放入的东西,其他线程取不到
3.2、 例:
--------------------------------------------------------------------------------------------------------------
public class ThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal tl = new ThreadLocal();
tl.set("pp"); // 向tl的Map中放入的一个对象pp 此时key为主线程,value为pp
Object obj = tl.get();
// 根据当前线程对象为key从ThreadLocal的Map中获取他绑定的值pp
System.out.println(obj); // pp
new AnotherThread(tl).start(); // null
}
}
public class AnotherThread extends Thread {
private ThreadLocal tl;
public AnotherThread(ThreadLocal tl){
this.tl = tl;
}
@Override
public void run() {
System.out.println("另外一个线程取东东: " + tl.get()); // 注: 取不到,为null
}
}
--------------------------------------------------------------------------------------------------------------
3.3、 应用:实际开发中使用ThreadLocal给事务绑定同一个connection进行事务控制
例:银行转账
--------------------------------------------------------------------------------------------------------------
use day17;
create table account(
id int primary key,
name varchar(100),
money float
);
insert into account (id,name,money) values(1,"aaa",1000);
insert into account (id,name,money) values(2,"bbb",1000);
insert into account (id,name,money) values(3,"ccc",1000);
--------------------------------------------------------------------------------------------------------------
// 目的: 使用线程局部变量类ThreadLocal让每个线程都绑定一个独立的conn
// 使得事务中使用的均是同一个conn 即在同一个事务中
// 把得到连接及事务有关的方法写到此类中
public class TransactionUtil {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private static DataSource ds;
static{
try{
InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(in);
ds = BasicDataSourceFactory.createDataSource(props);
}catch(Exception e){
e.printStackTrace();
}
}
public static DataSource getDataSource(){
return ds;
}
public static Connection getConnection(){
// 如果第一次访问,则没有conn,则由ThreadLocal中的map的线程到不到绑定的conn,
// 则由数据源先获取conn,再绑定到tl中
try {
Connection conn = tl.get();
if(conn==null){
conn = ds.getConnection();
tl.set(conn);
}
return conn;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void startTransaction(){
try {
Connection conn = tl.get();
if(conn==null){
conn = getConnection(); // 调用上面的方法getConnection()
}
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void roolback(){
try {
Connection conn = tl.get();
if(conn==null){
conn = getConnection();
}
conn.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void commit(){
try{
Connection conn = tl.get();
if(conn==null){
conn = getConnection();
}
conn.commit();
}catch(SQLException e){
throw new RuntimeException(e);
}
}
public static void release(){
try{
Connection conn = tl.get();
if(conn!=null){
conn.close();
tl.remove(); // 在Map中根据当前线程将该对象conn删除掉
}
}catch(SQLException e){
throw new RuntimeException(e);
}
}
}
--------------------------------------------------------------------------------------------------------------
// DBUtil框架中使用事务 : 此时若事务中的语句出异常,则会回滚,数据库中的数据不会变动,保证了数据的正确性
// DAO层不能牵扯任何的业务逻辑,所有的事务都在业务层 DAO层只负责增删改查
// 所有由DAO层负责查到账户,修改账户,则在业务层实现转账
public class AccountDaoImpl {
private QueryRunner qr = new QueryRunner();
public Account findAccount(String accountName){
try {
return qr.query(TransactionUtil.getConnection(),"select * from account where name=?",
new BeanHandler<Account>(Account.class), accountName);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void updateAccount(Account account){
try {
qr.update(TransactionUtil.getConnection(), "update account set money=? where name=?",
account.getMoney(),account.getName());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
--------------------------------------------------------------------------------------------------------------
// 优雅的代码: 在业务层看不到本应该存在于DAO层的conn
public class AccountServiceImpl {
private AccountDaoImpl dao = new AccountDaoImpl(); // 此处确定事务为同一个conn
public void transfer(String sourceAccountName,String targetAccountName,float money){
try{
TransactionUtil.startTransaction();
Account sAccount = dao.findAccount(sourceAccountName);
Account tAccount = dao.findAccount(targetAccountName);
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
dao.updateAccount(sAccount);
// int i = 1/0; // 用来模拟异常
dao.updateAccount(tAccount);
}catch(Exception e){
TransactionUtil.roolback();
e.printStackTrace();
}finally{
TransactionUtil.commit();
TransactionUtil.release();
}
}
}
--------------------------------------------------------------------------------------------------------------
public class Client {
public static void main(String[] args) {
AccountServiceImpl s = new AccountServiceImpl();
s.transfer("aaa", "bbb", 100);
}
}
--------------------------------------------------------------------------------------------------------------
并了解策略设计模式
二、开源的DBUtil框架的使用
2.1 需要的jar包:
commons-dbutils-1.4.jar
commons-dbcp-1.4.jar (使用DBCP数据源或C3P0数据源)
commons-pool-1.5.6.jar
mysql-connector-java-5.0.8-bin.jar (MySQL数据库驱动)
2.2 配置文件
DBCP所需要的配置文件: dbcpconfig.properties
----------------------------------------------------------------------------------------
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day16
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ
----------------------------------------------------------------------------------------
2.3 工具类
DBCP所需要的工具类DBCPUtil
----------------------------------------------------------------------------------------
public class DBCPUtil {
private static DataSource ds;
static{
try {
InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(in);
ds = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataSource getDataSource(){
return ds;
}
public static Connection getConnection(){
try {
return ds.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
----------------------------------------------------------------------------------------
2.4 DBUtil开源框架中QueryRunner的基本使用例子以及熟悉各种结果处理器的使用
-----------------------------------------------------------------------------------------
所需要的数据库:
create database day17;
use day17;
create table t1(
id int primary key,
name varchar(100),
birthday date
);
create table t2(
id int primary key,
content longtext
);
create table t3(
id int primary key,
content longblob
);
-----------------------------------------------------------------------------------------
// 例1: 完成增删改和批处理
public class DbUtilDemo1 {
private QueryRunner qr = new QueryRunner(DBCPUtil.getDataSource());
@Test
public void testAdd1() throws Exception{
// 注: 后面两个为可变参数,也可以用一个数组扩起来
qr.update("insert into t1 (id,name,birthday) values(?,?,?)", 1,"gfy",new Date());
}
@Test // 注: mysql中可以直接输入yyyy-MM-dd形式的日期格式
public void testAdd2() throws Exception{
qr.update("insert into t1 (id,name,birthday) values(?,?,?)", 2,"zqy","1994-02-23");
}
@Test
public void testUpdate() throws Exception{
qr.update("update t1 set birthday=? where name=?", "1990-08-08","zqy");
}
@Test
public void testDel() throws Exception{
qr.update("delete from t1 where id=?",1);
}
@Test // 批处理 向表中插入10条记录
public void testBatch() throws Exception{
// 二维数组: 第一位:记录的条数; 第二维:每条记录中需要的参数
Object params[][] = new Object[10][];
for(int i=0;i<10;i++){
params[i] = new Object[]{i+1,"aaa"+(i+1),new Date()};
}
qr.batch("insert into t1 (id,name,birthday) values(?,?,?)", params);
}
@Test // 存储大文本 注: 不建议使用,对于大的文本,内存可能溢出
public void testClob() throws Exception{
File file = new File("src/jpm.txt");
Reader r = new FileReader(file);
char[] ch = new char[(int) file.length()]; // 文本的长度
// 将文本写到数组中,记得关流
r.read(ch);
r.close();
Clob clob = new SerialClob(ch);
qr.update("insert into t2 (id,content) values(?,?)",1,clob);
}
@Test // 存储图片 注: 验证: select count(*) from t3; 千万不要用查询*来验证 好奇心害死猫
public void testBlob() throws Exception{
InputStream in = new FileInputStream("src/0.jpg");
byte[] b = new byte[in.available()];
in.read(b);
in.close();
Blob blob = new SerialBlob(b);
qr.update("insert into t3 (id,content) values(?,?)",1,blob);
}
}
-----------------------------------------------------------------------------------------
// 例2: 完成查询操作(注: javaBean中的Account类,字段有id,name,money)
并熟悉所有的结果处理器,若有其他需要,则可以自己实现处理器
public class DbUtilDemo2 {
private QueryRunner qr = new QueryRunner(DBCPUtil.getDataSource());
@Test // BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中
// 查询结果集中的记录 注: 只适合结果集中有一条记录的情况,若有多条记录,则只返回第一条
public void test1() throws SQLException{
Account a = qr.query("select * from account where id=?", new BeanHandler<Account>(Account.class), 1);
System.out.println(a);
}
@Test // BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中
// 查询结果集中的多条记录
public void test2() throws SQLException{
List<Account> list = qr.query("select * from account", new BeanListHandler<Account>(Account.class));
for(Account a:list){
System.out.println(a);
}
}
@Test // ArrayHandler: 把结果集中的第一行数据转换成对象数组,只适合结果集只有一条记录的情况
public void test3() throws SQLException{
// 该数组中每个元素就是记录的每列的值
Object[] objs = qr.query("select * from account where id=?", new ArrayHandler(), 1);
for(Object o : objs){
System.out.println(o);
}
}
@Test // ArrayListHandler: 把结果集中的第一行数据转换成对象数组,适合结果集中有多条记录的情况
public void test4() throws SQLException{
List<Object[]> list = qr.query("select * from account", new ArrayListHandler());
for(Object[] objs : list){
System.out.println("--------");
for(Object o : objs){
System.out.println(o);
}
}
}
@Test // ColumnListHandler: 将结果集中的某一列的数据存放到list中
public void test5() throws SQLException{
List<Object> list = qr.query("select * from account", new ColumnListHandler("name"));
for(Object o : list){
System.out.println(o);
}
}
@Test // MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
public void test6() throws SQLException{
Map<String,Object> map = qr.query("select * from account where id=?", new MapHandler(),1);
for(Map.Entry<String,Object> me : map.entrySet()){
System.out.println(me.getKey()+"="+me.getValue());
}
}
@Test // KeyedHandler(name):内部是一个双层Map 注: 要学会该双重Map集合的遍历
// 将结果集中的每一行数据都封装到一个Map<列名,列值>里,再将这些map存到一个Map中,其key为指定的key
public void test7() throws SQLException{
Map<Object,Map<String,Object>> bmap = qr.query("select * from account", new KeyedHandler("id"));
for(Map.Entry<Object, Map<String,Object>> bme : bmap.entrySet()){
System.out.println("----------");
Map<String,Object> lmap = bme.getValue();
for(Map.Entry<String, Object> lme : lmap.entrySet()){
System.out.println(lme.getKey()+"="+lme.getValue());
}
}
}
@Test // MapListHandler: 将结果集中的每一行数据都封装到一个map里,然后再存放到list
public void test8() throws SQLException{
List<Map<String,Object>> list = qr.query("select * from account", new MapListHandler());
for(Map<String,Object> map:list){
System.out.println("---------");
for(Map.Entry<String,Object> me: map.entrySet()){
System.out.println(me.getKey()+"="+me.getValue());
}
}
}
@Test // ScalarHander: 适合取一条一列的记录,比如记录总数
public void test9() throws SQLException{
Object obj = qr.query("select count(*) from account", new ScalarHandler(1));
System.out.println(obj.getClass().getName()); // 注: obj为long类型的
int num = ((Long)obj).intValue();
System.out.println(num);
}
}
-----------------------------------------------------------------------------------------
三、ThreadLocal类
3.1、 线程局部变量类ThreadLocal: 内部有一个Map, key为当前线程对象,value:为你放入的东东
特点: 只能本线程获取本线程放入的东西,其他线程取不到
3.2、 例:
--------------------------------------------------------------------------------------------------------------
public class ThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal tl = new ThreadLocal();
tl.set("pp"); // 向tl的Map中放入的一个对象pp 此时key为主线程,value为pp
Object obj = tl.get();
// 根据当前线程对象为key从ThreadLocal的Map中获取他绑定的值pp
System.out.println(obj); // pp
new AnotherThread(tl).start(); // null
}
}
public class AnotherThread extends Thread {
private ThreadLocal tl;
public AnotherThread(ThreadLocal tl){
this.tl = tl;
}
@Override
public void run() {
System.out.println("另外一个线程取东东: " + tl.get()); // 注: 取不到,为null
}
}
--------------------------------------------------------------------------------------------------------------
3.3、 应用:实际开发中使用ThreadLocal给事务绑定同一个connection进行事务控制
例:银行转账
--------------------------------------------------------------------------------------------------------------
use day17;
create table account(
id int primary key,
name varchar(100),
money float
);
insert into account (id,name,money) values(1,"aaa",1000);
insert into account (id,name,money) values(2,"bbb",1000);
insert into account (id,name,money) values(3,"ccc",1000);
--------------------------------------------------------------------------------------------------------------
// 目的: 使用线程局部变量类ThreadLocal让每个线程都绑定一个独立的conn
// 使得事务中使用的均是同一个conn 即在同一个事务中
// 把得到连接及事务有关的方法写到此类中
public class TransactionUtil {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private static DataSource ds;
static{
try{
InputStream in = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(in);
ds = BasicDataSourceFactory.createDataSource(props);
}catch(Exception e){
e.printStackTrace();
}
}
public static DataSource getDataSource(){
return ds;
}
public static Connection getConnection(){
// 如果第一次访问,则没有conn,则由ThreadLocal中的map的线程到不到绑定的conn,
// 则由数据源先获取conn,再绑定到tl中
try {
Connection conn = tl.get();
if(conn==null){
conn = ds.getConnection();
tl.set(conn);
}
return conn;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void startTransaction(){
try {
Connection conn = tl.get();
if(conn==null){
conn = getConnection(); // 调用上面的方法getConnection()
}
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void roolback(){
try {
Connection conn = tl.get();
if(conn==null){
conn = getConnection();
}
conn.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void commit(){
try{
Connection conn = tl.get();
if(conn==null){
conn = getConnection();
}
conn.commit();
}catch(SQLException e){
throw new RuntimeException(e);
}
}
public static void release(){
try{
Connection conn = tl.get();
if(conn!=null){
conn.close();
tl.remove(); // 在Map中根据当前线程将该对象conn删除掉
}
}catch(SQLException e){
throw new RuntimeException(e);
}
}
}
--------------------------------------------------------------------------------------------------------------
// DBUtil框架中使用事务 : 此时若事务中的语句出异常,则会回滚,数据库中的数据不会变动,保证了数据的正确性
// DAO层不能牵扯任何的业务逻辑,所有的事务都在业务层 DAO层只负责增删改查
// 所有由DAO层负责查到账户,修改账户,则在业务层实现转账
public class AccountDaoImpl {
private QueryRunner qr = new QueryRunner();
public Account findAccount(String accountName){
try {
return qr.query(TransactionUtil.getConnection(),"select * from account where name=?",
new BeanHandler<Account>(Account.class), accountName);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void updateAccount(Account account){
try {
qr.update(TransactionUtil.getConnection(), "update account set money=? where name=?",
account.getMoney(),account.getName());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
--------------------------------------------------------------------------------------------------------------
// 优雅的代码: 在业务层看不到本应该存在于DAO层的conn
public class AccountServiceImpl {
private AccountDaoImpl dao = new AccountDaoImpl(); // 此处确定事务为同一个conn
public void transfer(String sourceAccountName,String targetAccountName,float money){
try{
TransactionUtil.startTransaction();
Account sAccount = dao.findAccount(sourceAccountName);
Account tAccount = dao.findAccount(targetAccountName);
sAccount.setMoney(sAccount.getMoney()-money);
tAccount.setMoney(tAccount.getMoney()+money);
dao.updateAccount(sAccount);
// int i = 1/0; // 用来模拟异常
dao.updateAccount(tAccount);
}catch(Exception e){
TransactionUtil.roolback();
e.printStackTrace();
}finally{
TransactionUtil.commit();
TransactionUtil.release();
}
}
}
--------------------------------------------------------------------------------------------------------------
public class Client {
public static void main(String[] args) {
AccountServiceImpl s = new AccountServiceImpl();
s.transfer("aaa", "bbb", 100);
}
}
--------------------------------------------------------------------------------------------------------------
相关文章推荐
- ListView--QQ联系人样式
- UIWindow
- LeetCode_OJ【21】Merge Two Sorted Lists
- HDU 1754 线段树
- android解析JSON,XML数据的请求网络工具类
- 类的一般继承与虚继承
- 国外程序员整理的Java资源大全
- HDU 1394(逆序数)
- 设计模式 观察者模式 以微信公众服务为例
- flex通过HTTPService与java通信例子
- UIApplication
- 药品搜索开发API接口-查询药品信息
- 微店
- 商业计划书(BP)应该包含哪些点?看 BP 的人最想从中得到什么?
- shellcode基础(1)
- 随波逐流之IOS数据库的基本操作
- 初识Android
- Java中hashcode,equals和==
- Python学习笔记 (一)
- 【CentOS 6.5】解决QtCreator 安装时错误: 无法解析dbus_connection_can_send_type中的符号"dbus_connection_can_send_type"