DBUtils以及相关案例 ThreadLocal概述 转账业务案例

一、DBUtils介绍 apache

可以仔细看下这篇博文:https://www.cnblogs.com/xdp-gacl/p/4007225.html

什么是dbutils,它的作用

DBUtils是java编程中的数据库操作实用工具,小巧简单实用。
DBUtils封装了对JDBC的操作,简化了JDBC操作。可以少写代码。
1.对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
2.对于数据表的写操作,也变得很简单(只需写sql语句)
3.可以使用数据源,使用JNDI,数据库连接池等技术来优化性能–重用已经构建好的数据库连接对象

使用DBUtils框架

* 它是Apache开发的一个框架,所以需要导包*
方法:
QueryRunner类

它主要有三个方法

  • query() 用于执行select
  • update() 用于执行insert update delete
  • batch() 批处理

ResultSetHandler接口(可以用它的实现类)
用于定义select操作后,怎样封装结果集.

查询:

//创建一个QueryRunner对象(获取DBCPUtil的数据源     DBCPUtil要用一个getter()方法)
  QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
  //参数分别为 sql语句    BeanListHandler封装结果集    可变参数 Object... object
            List<User> list = qr.query("select * from user where username=? and password = ?",new BeanListHandler<User>(User.class),"tony","123");

增加

QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
//参数  sql语句   可变参数
     int i = qr.update("insert into user(username,password)values(?,?)","laowang","123");

删除

QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
//参数  sql语句   可变参数
 int i = qr.update("delete from user where id=?",5);

修改

 QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
 //参数  sql语句   可变参数
 int i = qr.update("update user set username = ? where username = ?","laolu","jerry");

使用DBUtils框架的demo

DBCPUtil 连接池:

package commons;

import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class DBCPUtil {
    //数据源
    private  static  DataSource ds=null;

    public static DataSource getDs() {
        return ds;
    }

    static {
        //Properties类是用来操作properties文件的
        Properties prop = new Properties();
        try {
            //通过类加载器加载你的配置文件
            prop.load(DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));
            //一个工厂创建一个数据源    把配置文件加载进来就知道有多少个连接了
             ds = BasicDataSourceFactory.createDataSource(prop);//得到一个数据源
        } catch (Exception e) {
           throw  new ExceptionInInitializerError("初始化错误,请检查配置文件");
        }
    }

    public static Connection getConnection(){
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            throw  new RuntimeException("服务器忙。。。");
        }
    }
   public static  void realse(Connection conn, PreparedStatement ps, ResultSet rs){
        if (conn!=null){
            try {
                //1.如果是从连接池中拿的 就是释放
                //2.如果是新创建的就直接关闭
                conn.close();//关闭
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn =null;
        }
        if(ps!=null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            ps=null;
        }
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
   }
}

DBUtils

package commons;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import pojo.User;

import java.sql.SQLException;
import java.util.List;

public class DBUtils {
    public static void main(String[] args) throws SQLException {
       DBUtils dbUtils = new DBUtils();
       dbUtils.testSelect();
       dbUtils.testSelect2();
       dbUtils.testInsert();
        dbUtils.testDelete();
        dbUtils.testUpdate();
    }

    //查询
    public  void testSelect() throws SQLException {
        //创建一个  QueryRunner对象
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
            //执行查询语句  并返回结果         BeanListHandler 往某个类里面封装对象   后面是可变参数
            List<User> list = qr.query("select * from user",new BeanListHandler<User>(User.class));
//              return list;
        for (User user:list) {
            System.out.println(user);
        }
    }

    public void testSelect2() throws SQLException {
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
            List<User> list = qr.query("select * from user where username=? and password = ?",new BeanListHandler<User>(User.class),"tony","123");
        for (User user:list) {
            System.out.println(user.getUserName());
        }
    }

    public void testInsert() throws SQLException {
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        int i = qr.update("insert into user(username,password)values(?,?)","laowang","123");
        if (i>0){
            System.out.println("操作成功");
        }else {
            System.out.println("操作失败");
        }
    }
    public void testDelete() throws SQLException {
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        int i = qr.update("delete from user where id=?",5);
        if (i>0){
            System.out.println("操作成功");
        }else {
            System.out.println("操作失败");
        }
    }
    public void testUpdate() throws SQLException {
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        int i = qr.update("update user set username = ? where username = ?","laolu","jerry");
        if (i>0){
            System.out.println("操作成功");
        }else {
            System.out.println("操作失败");
        }
    }
}



以上代码是使用 DButils 框架完成的增删改差批处理。
其中,使用 了 QueryRunner 这个类,这个类是专门负责处理 sql 语句的,有四个构造方法,我们经常使用到的是一个无参的构造方法和一个有参的, 参数就是数据源, 当我们给其提供数据源的时候 就是让框架为我们自动的创建数据库连接, 并释放连接, 当这是处理一般操作的时候.
当我们要进行事务处理的时候, 那么连接的释放就要由我们自己来决定了, 所以我们就不再使用带参数的 QueryRunner 构造方法了。


ResultSetHandler接口

  1. ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
  2. ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
  3. ColumnListHandler:取某一列的数据。封装到List中。
  4. KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值。(仔细看以下代码)
    这里写图片描述

  5. MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中

  6. MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中
  7. ScalarHandler:适合取单行单列数据
  8. BeanHandler 适合取单行数据 返回为实体类型
  9. BeanListHandler 适合取多行数据 返回为list 泛型为实体类型


    代码案例:
package commons;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;
import pojo.User;


import javax.jws.soap.SOAPBinding;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;


public class TestResultSetHandler {
    public static void main(String[] args) throws SQLException {
TestResultSetHandler t1 = new TestResultSetHandler();
//                t1.test1();
//                t1.test2();
//                t1.test3();
//                t1.test4();
//                t1.test5();
//                t1.test6();
//                t1.test7();
//                   t1.test8();
        t1.test9();
    }


    public void test1() throws SQLException {
        //ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        Object[] arr = qr.query("select * from user where id = ?",new ArrayHandler(),3);
        for (Object o:arr) {
            System.out.println(o);
        }
    }
    public void test2() throws SQLException {
        //ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        List<Object[]> list1= qr.query("select * from user",new ArrayListHandler());
        for (Object[] a:list1) {//第一层循环list
            for(Object b:a)//第二层循环每一层数组里面的值
                System.out.println(b);
            System.out.println("---------------------------");
        }
    }
    public void test3() throws SQLException {
        //ColumnListHandler:取某一列的数据。封装到List中。
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        List<Object> list2 = (List<Object>) qr.query("select * from user",new ColumnListHandler(2));
//另一中写法
//        List<Object> list2 = (List<Object>) qr.query("select name ,password from user",new ColumnListHandler("name"));
        for (Object o:list2){
            System.out.println(o);
        }
    }
    public void test4() throws SQLException {
        //KeyedHandler:取多条记录,每一条记录封装到一个小Map中,key为字段名,value为值
        // 再把这个小Map封装到另外一个大Map中,key为用户指定的某一列数据 ,value为每个小map
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
     //大Map的key是表中的某列数据,小Map中的key是表的列名,所以大Map的key是object,小Map中的key是String
      Map<Object,Map<String,Object>> mapMap= (Map<Object, Map<String, Object>>) qr.query("select * from user",new KeyedHandler(1));
        for (Map.Entry<Object,Map<String,Object>> m:mapMap.entrySet()) {
            for (Map.Entry<String,Object> mm:m.getValue().entrySet()) {
                System.out.println(mm.getKey()+" "+mm.getValue());
            }
        }
    }
    public void test5() throws SQLException {
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        //MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中
        Map<String,Object> map = qr.query("select * from user where id = ?",new MapHandler(),3);
        for (Map.Entry<String,Object> m:map.entrySet()) {
            System.out.println(m.getKey()+" "+m.getValue());
        }
    }
    public void test6() throws SQLException {
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        //MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中
        List<Map<String,Object>> mapList = qr.query("select * from user",new MapListHandler());
        for (Map<String,Object> map:mapList) {
            for (Map.Entry<String,Object> m:map.entrySet()){
                System.out.println(m);
            }
        }
    }
    public void test7() throws SQLException {
        //ScalarHandler:适合取单行单列数据   一个单元格
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        Object o = qr.query("select * from user",new ScalarHandler<Object>(2));
        Long o1 =  qr.query("select count(*) from user",new ScalarHandler<Long>(1));
        System.out.println(o1);//       4
        System.out.println(o1.getClass().getName());              //java.lang.Long
    }
    public void test8() throws SQLException {
        // BeanHandler   取一个泛型对象 返回类型为对象的类型
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
       User user = qr.query("select * from user where id = ?",new BeanHandler<User>(User.class),3);
        System.out.println(user);
    }
    public void test9() throws SQLException {
        // BeanListHandler   适合取多行数据    返回为list 泛型为对象类型
        QueryRunner qr = new QueryRunner(DBCPUtil.getDs());
        List<User> userList= qr.query("select * from user ",new BeanListHandler<User>(User.class));
        for (User user:userList){
            System.out.println(user);
        }
    }
}

ThreadLocal类(转账业务中控制同一个事务取得的是同一个连接)

模拟ThreadLocal的设计,让大家明白他的作用。
public class ThreadLocal{
private Map<Runnable,Object> container = new HashMap<Runnable,Object>();
public void set(Object value){
//若当前线程value为connection
container.put(Thread.currentThread(),value);//用当前线程作为key
}
public Object get(){
//则根据key(即当前线程)得到的为同一个连接
return container.get(Thread.currentThread());
}
public void remove(){
container.remove(Thread.currentThread());
}
}

总结:调用该类的get方法,永远返回当前线程放入的数据。线程局部变量。

测试案例

  • 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
  • 应用场景:当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal。比如说Dao的数据库连接。

只有当前线程可以访问的测试案例:
TestThreadLocal.java

public class TestThreadLocal {
    public static void main(String[] args){
       ThreadLocal tl = new ThreadLocal();
        tl.set("p");
        MyThread mt = new MyThread(tl);//开启另一个线程
        mt.start();      //不是同一个线程即使传过去了也取不到           nullaaaaaaaaaaaa
        System.out.println(tl.get());//当前线程的对象                 p
    }
}

MyThread.java

import com.sun.xml.internal.stream.util.ThreadLocalBufferAllocator;

public class MyThread extends Thread {
    private ThreadLocal tl;
   public MyThread(ThreadLocal tl){
     this.tl = tl;
   }
    public void run(){
        System.out.println(tl.get()+"aaaaaaaaaaaa");
    }
}

转账业务案例

/*****
 * 分层思想   dao service commons pojo
 * 准备环境 : mysql连接数据库   dbcp数据源    dbUtils工具
 *  dao:  根据账户更新账户信息   根据姓名查询账户
 *  service:  调用dao层findByName()获取fromAccount  toAccount
 *            调用dao层updateAccount()更新账户信息
 *  commons:MangerThreadLocal类控制连接   控制事务(转账业务两个sql语句是同一个事务需要同一个连接)
 * */

代码如下:(dao层和service层的接口代码略)
AccountDaoImpl.java

package dao.impl;

/*****
 * 分层思想   dao service commons pojo
 * 准备环境 : mysql连接数据库   dbcp数据源    dbUtils工具
 *  dao:  根据账户更新账户信息   根据姓名查询账户
 *  service:  调用dao层findByName()获取fromAccount  toAccount
 *            调用dao层updateAccount()更新账户信息
 *  commons:MangerThreadLocal类控制连接   控制事务(转账业务两个sql语句是同一个事务需要同一个连接)
 * */
import commons.ManagerThreadLocal;
import dao.AccountDao;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import pojo.Account;

public class AccountDaoImpl implements AccountDao {
    public void updateAccount(Account account) throws Exception {
        QueryRunner qr = new QueryRunner();
        qr.update(ManagerThreadLocal.getConnection(),"update account set money=? where name =?",account.getMoney(),account.getName());
    }


    public Account findByName(String name) throws Exception {
        QueryRunner qr = new QueryRunner();
        return qr.query(ManagerThreadLocal.getConnection(),"select * from account where name = ?",new BeanHandler<Account>(Account.class),name);
    }
}

AccountServiceImpl.java

package service.impl;

import commons.DBCPUtil;
import commons.ManagerThreadLocal;
import dao.AccountDao;
import dao.impl.AccountDaoImpl;
import pojo.Account;
import service.AccountService;

import java.sql.Connection;
import java.sql.SQLException;

public class AccountServiceImpl implements AccountService {

    public void transfer(String fromname, String toname, int money) {
//        Connection conn = DBCPUtil.getConnection();
//        AccountDao ad = new AccountDaoImpl(conn);//保证拿到的是同一个conn
        AccountDao ad = new AccountDaoImpl();
        try {
           ManagerThreadLocal.startTransaction();//开启事务
            //分别得到转出和转入账户对象
            Account fromAccount = ad.findByName(fromname);
            Account toAccount = ad.findByName(toname);
            //修改账户的金额
            fromAccount.setMoney(fromAccount.getMoney()-money);
            toAccount.setMoney(toAccount.getMoney()+money);
            //完成转账操作
            ad.updateAccount(fromAccount);// 一个事务只能有一个连接  而这两句话创建了两个连接
//            int i = 10/0;
            ad.updateAccount(toAccount);
            ManagerThreadLocal.commit();
        }catch (Exception e){
            try {
               ManagerThreadLocal.rollback();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }finally {
            try {
               ManagerThreadLocal.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

ManagerThreadLocal.java(控制事务和连接)

package commons;

import java.sql.Connection;
import java.sql.SQLException;

//拿到连接  控制事务
public class ManagerThreadLocal {

    private static  ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
     //获取连接
    public static Connection getConnection(){
        Connection conn = tl.get();
        if (conn==null){
            conn = DBCPUtil.getConnection();
            tl.set(conn);//把conn对象放入当前线程对象中
        }
        return conn;
    }

 //开启事务
    public static void startTransaction(){
        try {
            Connection conn = getConnection();
            conn.setAutoCommit(false);//从当前线程对象中取出连接并开启事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void commit(){
        try {
            getConnection().commit();//提交事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void rollback(){
        try {
            getConnection().rollback();//回滚事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void close(){
        try {
            getConnection().close();//把连接放回池中
            tl.remove();//把当前线程的conn移除  不然每次都往线程中放key  value
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

主方法测试:
TestTransfer.java

import service.AccountService;
import service.impl.AccountServiceImpl;

public class TestTransfer {
    public static void main(String[] args){
        AccountService as = new AccountServiceImpl();
        as.transfer("aaa","bbb",100);
    }
}

注意::在ManagerThreadLocal中close方法不仅需要释放conn,还要tl.remove(),移除当前线程中的conn,不然就会一直放key value进去

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值