一、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接口
- ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
- ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
- ColumnListHandler:取某一列的数据。封装到List中。
KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值。(仔细看以下代码)
MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中
- MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中
- ScalarHandler:适合取单行单列数据
- BeanHandler 适合取单行数据 返回为实体类型
- 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进去