DBUtils

本文详细介绍了C3P0连接池的配置及使用方法,并深入探讨了DBUtils库的功能及其在Java应用程序中的应用实践。

C3P0相关

一、c3p0 jar包导入

c3p0 jar包下载,密码:ngu0

二、编写配置文件

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/数据库名称</property>
    <property name="user">root</property>
    <property name="password">root</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>
    <property name="maxStatements">200</property>
</default-config>
</c3p0-config>

注意事项:

1.xml配置文件名为c3p0-config.xml
2.默认配置可参考,修改数据库用户,密码,在jdbcUrl那栏补全数据库名称即可。
3.xml文件放在src路径下

三、C3P0工具类的编写

package com.itlc.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class C3P0Util {
    private static ComboPooledDataSource dataSource=new ComboPooledDataSource();

    public static ComboPooledDataSource getDataSource() {
        return dataSource;
    }

    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        }catch (SQLException e){
            throw new RuntimeException("创建失败");
        }
    }
    public static void release(ResultSet rs, Statement stmt,Connection conn){
        if(rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

注意:若跳SQL异常报创建失败,优先检查xml文件

DBUtil相关

一、DBUtils介绍

DBUtils是java编程中的数据库操作实用工具,小巧简单实用。
DBUtils封装了对JDBC的操作,简化了JDBC操作。可以少写代码。

1.对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;

2.对于数据表的写操作,也变得很简单(只需写sql语句)

3.可以使用数据源,使用JNDI,数据库连接池等技术来优化性能–重用已经构建好的数据库连接对象

二、DBUtils的三个核心对象

QueryRunner类
ResultSetHandler接口
DBUtils类

QueryRunner类
QueryRunner中提供对sql语句操作的API.
它主要有三个方法
query() 用于执行select
update() 用于执行insert update delete
batch() 批处理

ResultSetHandler接口
用于定义select操作后,怎样封装结果集.

DbUtils类
它就是一个工具类,定义了关闭资源与事务处理的方法

三、导jar包

DBUtils jar包下载,密码:kpcf

四、查询语句

 QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            List<User> list=qr.query("select * from user",new BeanListHandler<User>(User.class));
            for (User user:list) {
                System.out.println(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

五、update函数

1.insert

 QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            qr.update("insert into test(id,password,name) values(?,?,?)","123","456","789");
        } catch (SQLException e) {
            e.printStackTrace();
        }

传参时sql语句后面接param,对应sql的?

2.update

 QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            qr.update("update test set id=?,password=?,name=? where id=123","abc","def","ghk");
        } catch (SQLException e) {
            e.printStackTrace();
        }

传参时sql语句后面接param,对应sql的?

3.delete

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            qr.update("delete from test where id=?","abc");
        } catch (SQLException e) {
            e.printStackTrace();
        }

传参时sql语句后面接param,对应sql的?

六、batch函数多次执行sql语句

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        Object[][] params=new Object[10][];//高维表示有多少组params,执行多少次sql
        for(int i=0;i<10;i++) {
            params[i]=new Object[]{"abc"+(i+1),"bcd","efg"};
        }
        try {
            qr.batch("insert into test(id,password,name) values(?,?,?)",params);
        } catch (SQLException e) {
            e.printStackTrace();
        }

注意事项:操作语句前面都可以加一个Connection,则使得操作时默认使用这个Connection,不加默认从连接词里面取一个连接

ResultSetHandler下的所有结果处理器

1.ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
//返回的数组只能查询到的第一行的数据

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            Object[] o=qr.query(sql,new ArrayHandler(),params);
            for (Object o1:o){
                System.out.println(o1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

2.ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
//返回的集合里面存有多个Object数组,每个数组储存一行数据

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            List<Object[]> list=qr.query(sql语句,new ArrayListHandler(),params);
            for (Object[] o1:list){
                System.out.println(o1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

3.ColumnListHandler:取某一列的数据。封装到List中。
//内部传参一个int数据n,表示返回的list里面存的是搜索到的第n列数据,不传参默认存第一列,n大于搜索到的总列数时跳SQLException。

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            List<Object> list=qr.query(sql语句,new ColumnListHandler(n),params);
            for (Object o:list){
                System.out.println(o);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

4.KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值。
外部图的key由用户自己定义,一般为在创建KeyedHandler时传入1表示阿拉伯数字为键,value为多个map,内部map中键为id,password等表中列名,内部value为键对应的值,每行数据成一个内部map。
//Set< Map.Entry< K,V > > set=map.entrySet();用该方法返回一个集合,储存的是对象,每个对象都存储了一对映射,可通过迭代器,迭代每一个对象使用getKey(),getValue()来获得键和值。

 QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            Map<Object,Map<String,Object>> map=qr.query(sql语句,new KeyedHandler(1),params);
            for(Map.Entry<Object,Map<String,Object>> m:map.entrySet()){
            /*map.entrySet()返回一个set集合,而集合中每个元素的类型是Map.Entry<Object,Map<String,Object>>返回的对象*/
                for(Map.Entry<String,Object> mm:m.getValue().entrySet()){
                    /*m的值为一个map集合,则m.getValue().entrySet()是一个set集合,内部元素为Map.Entry<String,Object>返回的类*/
                    System.out.println(mm.getKey()+"   "+mm.getValue());/*在这个元素中得到的键值对为最后的目标*/
                }
                System.out.println("-----------------------");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

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

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            Map<String,Object> map=qr.query(sql语句,new MapHandler(),params);
            for (Map.Entry<String,Object> m:map.entrySet()){
                System.out.println(m.getKey()+"   "+m.getValue());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

6.MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            List<Map<String,Object>> list=qr.query(sql语句,new MapListHandler(),params);
            for (Map<String,Object> l1:list){
                for (Map.Entry<String,Object> m:l1.entrySet()){
                    System.out.println(m.getKey()+"   "+m.getValue());
                }
                System.out.println("-------------------");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

7.ScalarHandler:适合取单行单列数据
返回一个object值适合用于查询返回一个值时,比如count

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            Object o=qr.query("select count(*) from test",new ScalarHandler());
            System.out.println(o.toString());
        } catch (SQLException e) {
            e.printStackTrace();
        }

8.BeanHandler
返回一个对象,查询是多行数据时,只返回第一行数据封装进去的对象
对象数据需要完整的set方法,且数据名称,数量等需要严格对应的相等

QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            User user=qr.query(sql语句,new BeanHandler<User>(User.class),params);
                System.out.println(user);
        } catch (SQLException e) {
            e.printStackTrace();
        }

9.BeanListHandler
直接把数据每行封装进对象,返回对象数列,对象数据需要完整的set方法,且数据名称,数量等需要严格对应的相等

 QueryRunner qr=new QueryRunner(C3P0Util.getDataSource());
        try {
            List<User> list=qr.query(sql语句,new BeanListHandler<User>(User.class),params);
            for (User user:list) {
                System.out.println(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

注意事项:若使用BeanHandler或者BeanListHandler 查询时返回的集合中的对象数据全为null,可能是domain中用于封装的类中set方法返回了this,这时将返回类型改成void并删去return可能可以避免这个异常,在idea中导入的set方法全部默认有this返回,这时set方法在反射中存在差错导致数据无法成功封装(当然这只是一种可能的情况,也可能产生错误的原因不是这个)

事务

一、事务介绍

当数据库操作涉及两边同时操作时,列入两人的转账,在一人扣费,另一人为增加余额的过程中跳异常或其他操作导致转账中断,使得一人扣费而另一人未得到钱款的情况,这个时候使用事务,类似撤回操作,撤销上一步的数据库操作,取消转账人的扣费行为

二、分步代码

Connection.setAutoCommit(false)//start
Connecion.rollback()//撤回到start的地方
在catch中加入这个语句,若中途产生异常则会回滚,本语句也需要try
catch块,使用前确定connection是否为空
Connection.commit()//结束
注意:此处需要保证使用的是同一个连接才可以达到目的。

三、特性:

原子性:同时生效或同时不生效
一致性:保证start到end之间的数据库操作,要不都发生,要不都不发生
隔离性:每个业务相互独立,多个并发业务相互独立,多线程的使用
持久性:一旦到达end提交数据,数据的修改是永久性的

四、隔离级别:

数据库操作可能发生情况
1.脏读:读取到了另一个业务中为提交的数据,当另一个业务未end,但数据操作正在进行的时候,另一个业务读取数据,读取到修改了但未提交的数据,而第一个业务最后回滚了数据,也就使得第二个业务读到了不准确的数据。

2.不可重复读(updata):一个业务读取某个数据,多次读取结果不同,一个业务读取到另一个业务中提交后的数据,这也是不好的,在一个业务进行到一般时数据变化是体验度低的,应该保证在一次业务进行途中数据不因为另一个业务的修改数据而直接影响到自己这边的数据,数据反馈一个出现在自己这边业务提交之后。

3.虚读(幻读):一个业务读取到了另一个业务插入的数据,提交以后。

数据库通过设置事务的隔离级别防止以上情况的发生

1.READ UNCOMMITTED:脏读,不可重复读,虚读都有可能发生|||
2.READ COMMITTED:避免脏读。|||不可重复读,虚读都有可能发生(oracle默认)
3.REPEATABLE READ:避免脏读,不可重复读。|||虚读有可能发生(mysql默认)
4.SERIALIZABLE:|||避免脏读,不可重复读,虚读
级别越高性能越低数据越安全

设置隔离级别:必须在开启事务之前。
Connection.setTransactionIsolation(int level);
level:Connection.TRANSACTION_常量名

5.ThreadLocal

在dao层无法保证使用的是同一个连接,也就无法保证事务的正常使用,而在业务层使用C3P0Utils获得一个连接又不符合MVC模式的思想,这时使用ThreadLocal也就保证了一个线程只使用一个连接

模拟ThreadLocal的设计,方便明白他的作用。

public class ThreadLocal{
    private Map<Runnable,Object> container = new HashMap<Runnable,Object>();
    public void set(Object value){
        container.put(Thread.currentThread(),value);//用当前线程作为key
    }
    public Object get(){
        return container.get(Thread.currentThread());//返回当前容器中存储的连接
    }
    public void remove(){
        container.remove(Thread.currentThread());//结束时关闭连接与线程的关系
    }
}

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

使用当前线程作为键,值放连接,则在每个线程里面也就只会获得自己的连接,既保证了事务的正常使用,又符合MVC的思想。

自定义一个类似工具类的类,在类中定义一个ThreadLocal的对象,用这个对象获取和设置连接。并在类中写事务开始,回滚,结束的方法,便于业务层可直接使用

package com.itlc.util;

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 = C3P0Util.getConnection();//若未绑定连接,从池中取出一个
            tl.set(conn);//把conn对象放入到当前线程对象中,建立键值对关系
        }
        return conn;
    }

    //开始事务
    public static void startTransacation(){
        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();///结束时将他们从集合中删去,关闭连接与线程的关系,如果不切断联系,可能出现该用户下次使用时默认的再次使用这个连接,并且这个连接因为放回了线程池,可能又被别人使用,导致出现安全问题
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值