您的位置:首页 > 其它

开源框架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);

}

}

--------------------------------------------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: