JDBC工具类

1、Service事务(DAO层可以轻易进行事务管理)

JdbcUtils初始代码:

public class JdbcUtils {
    //使用配置文件的默认配置(c3p0-config.xml)
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource() ;
    //使用连接池返回一个连接对象
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection() ;
    } 
    //返回连接池对象
    public static DataSource getDataSource() {
        return dataSource ;
    }
}

在Service中进行事务管理同时为保证Connection对象的一致性可以将Dao层的连接对象作为参数传递过来,但是,一般不会在Service层进行数据库操作。所以需要完善JdbcUtils:

private static Connection con = null;

//开启事务:获取一个Connection,设置它的setAutoCommit(false),还要保证dao中使用的连接是我们刚刚创建的
public static void beginTransaction() {
    if(con != null) throw new SQLException("已经开启了事务,不要重复开启!") ;
    con = getConnection() ;
    con.setAutoCommit(false) ;
}
//提交事务:获取beginTransaction提供的Connection,然后调用commit方法
public static void commitTransaction() throws SQLException {
    if(con == null) throw new SQLException("还未开启事务,不能提交!") ;
    con.commit() ;
    con.close() ;
    con = null ; //因为上述close()方法只是将连接对象放回连接池,并没有真正关闭连接,所以需要手动设置连接对象为null,以保证下次连接的是新对象。
}
//回滚
public static void rollbackTransaction() {
    if(con == null) throw new SQLException("还未开启事务,不能回滚!") ;
    con.rollback() ;
    con.close() ;
    con = null ;
}

//此时DAO层连接也需要从JdbcUtils中获得,从而保证DAO层与业务层获取到的都是同一个连接

//使用连接池返回一个连接对象,通过判断可以保证在Service层调用该方法时获取到的是同一对象
public static Connection getConnection() throws SQLException {
    if(con != null) return con; //此时表明调用过beginTransaction()方法开启了事务
    return dataSource.getConnection() ;

2、JNDI配置

JNDI是Java命名和目录接口。作用:在服务器上配置资源,然后通过统一的方式来获取配置的资源。

示例:Tomcat配置连接池

<Context …>
    …
    <Resource name="jdbc/dataSource"
        type="com.mchange.v2.c3p0.ComboPooledDataSource"
        factory="org.apache.naming.factory.BeanFactory"
        jdbcUrl="" driverClass="" user="" password=""
        acquireIncrement="" initialPoolSize=""
     />
</Context>
配置JNDI资源需要到<Context>元素中配置<Resource>子元素
其中,name:指定资源的名称
factory:用来创建资源的工厂,值基本固定,不用修改
type:资源得类型,当前使用的是连接池的类型
bar:表示资源的属性,需要才配置。对于DBCP连接池而言需要配置的属性就是url、username等属性

>获取资源

导入jar包:(c3p0连接池:用来管理连接对象)c3p0-0.9.2-pre1.jar;mchange-commons-0.2.jar;(数据库驱动程序包)mysql-connector-Java-3.1.11-bin.jar

try{
    //创建JNDI的上下文对象
    Context cxt = new InitialContext();
    //查询出入口
    Context envContext = (Context)cxt.lookup("java:comp/env");
    //二次查询,找到资源,资源名称与<Resource>元素的name对应
    DataSource dataSource = (DataSource)envContext.lookup("jdbc/dataSource");
    //得到连接
    Connection con = dataSource.getConnection();
    ……
}catch(Exception e){
    throw new RuntimeException(e);
}

注://查找资源时可以将两次查找合并成一句:

DataSource dataSource = (DataSource)cxt.lookup("java:comp/env/jdbc/dataSource");

3、ThreadLocal类

该类内部是Map,键为线程Thread(保存值的方法内部map.put(Thread.currentThread(),data)),值是泛型值,所以该类的最大特点是多个线程存取互不影响。

该类中的三个方法:void set(T value) 保存值;T get() 获取值;void remove() 移除值

ThreadLoca<String> t = new ThreadLoca<String>()

4、JDBC操作数据库时,除了sql语句与参数不同外,其他初始化对象部分等均相同,所以可以考虑将其他部分进行包装:

类实现:

public class QR<T> {
    private DataSource dataSource ;
    //外部调用构造方法时获取连接池
    public QR(DataSource dataSource) {
        this.dataSource = dataSource ;
    }
    public QR() {
        super() ;
    }
    //外部在实例化对象(创建对象时给出连接池)后,需要给出sql语句与参数,而后调用方法执行
    public int update(String sql, Object...params) {
        Connection con = null ;
        PreparedStatement pstmt = null ;
        try {
            con = dataSource.getConnection() ;
            pstmt = con.PrepareStatement(sql) ;
            initParams(pstmt,params) ; //调用方法传递sql语句中参数
            return pstmt.executeUpdate() ;
        }catch(Exception e) {
            throw new RuntimeException(e) ;
        }finally {            
            try {
                if(pstmt != null) pstmt.close() ;
                if(con != null) con.close() ;
            }catch(SQLException e) {
                e.printStackTrace() ;
            }
        }
    }
    public T query(String sql,RsHandler rh, Object...params) {
        Connection con = null ;
        PreparedStatement pstmt = null ;
        ResultSet rs = null ;
        try {
            con = dataSource.getConnection() ;
            pstmt = con.PrepareStatement(sql) ;
            initParams(pstmt,params) ; //调用方法传递sql语句中参数
            rs = pstmt.executeQuery() ;
            return rh.handle(rs) ; //返回被方法处理过的结果
        }catch(Exception e) {
            throw new RuntimeException(e) ;
        }finally {            
            try {
                if(pstmt != null) pstmt.close() ;
                if(con != null) con.close() ;
            }catch(SQLException e) {
                e.printStackTrace() ;
            }
        }
    }
    private void initParams(PreparedStatement pstmt,Object...params) throws Exception{
        for(int i = 0; i < params.length; i++) {
            pstmt.setObject(i+1, params[i]) ;
        }
    }
}
//用来把结果集转换成所需对象类型
interface RsHandler<T> {
    public T handle(ResultSet rs) ;
}

 

//在类中实现RsHandle接口
RsHandler<Student> rh = new RsHandler<Student>() {
    public Student handle(ResultSet rs) {
        if(!rs.next()) return null ;
        Student stu = new Student() ;
        stu.setSid(rs.getInt("")) ;
        ...
        return stu ;
    }
}
其实,commons-dbutils-1.4.jar包中提供有解决该问题的工具类:QueryRunner类

调用方法示例:

QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource()) ;
String sql = "SELECT * FROM table WHERE id=?" ;
Object[] params = {1001} ;
//注意与上述自定义的处理方法不同点在于,执行query()方法,需要给出结果集处理器,即ResultSetHandler的实现类对象
//我们给的是BeanHandler,它实现了ResultSetHandler
//它需要一个类型,然后它会把rs中的数据封装到指定类型的javabean对象中,然后返回

Student stu = qr.query(sql, new BeanHandler<Student>(Student.class),params);
System.out.println(stu) ;

该类中的主要方法:

int update(Sstring sql , Object...params)

重载:int update(Connection con, Sstring sql , Object...params)需要调用者提供Connection,本方法不再管理Connection,即支持事务(保证了同一事务调用同一对象)

T query(String sql,ResultSetHandler rsh, Object...params)

重载:T query(Connection con,String sql,ResultSetHandler rsh, Object...params)

类中接口ResultSetHandle接口:

BeanHandler(单行):构造器需要一个Class类型参数,用来把一行结果转换为指定类型的javaBean对象

BeanListHandler(多行):构造器需要一个Class类型参数,用来把一行结果集转换为一个JavaBean,多行即为List对象

    List<Student> stuList =queryrunner.query(sql, new BeanListHandler<Student>(Student.class));

MapHandler(单行):把一行结果集转换为Map对象

MapListHandler(多行):把一行记录转为一个Map,多行就是多个Map,即List<Map<String,Object>>

ScalarHandler(单行单列):返回一个Object

存在的拆箱问题:单行单列返回值得类型,若为数值型:

Number cnt = (Number)qr.query(sql,new ScalarHandler());

long c = cnt.longValue();

//查看单行单列返回值的类型

Object obj = qr.query(sql,new ScalarHandler());

输出obj.getClass().getName() 

5、参照2,完善JdbcUtils:

添加释放连接方法:
public static void releaseConnection(Connection connection) throws SQLException {
    /*
    *判断其是否为事务专用,如果是,就不关闭。如果不是事务专用就关闭
    */
    if(con == null) Connection.close() ; //此时没有事务
    if(con != connection) Connection.close() ; //判断传递过来的连接对象是否是开启事务时创建的连接对象
}

6、JdbcUtils处理多线程并发问题(ThreadLocal)

//修改事务专用连接
private static ThreadLocal<Connection> t1 = new ThreadLocal<Connection>();

在执行开启事务时:
先使线程获取自身连接:Connection con = t1.get();
获取连接后(con=getConnection();)把当前线程的连接保存起来:t1.set(con);
在提交事务时:
先获取当前线程的专用连接:Connection con = t1.get();
提交事务后(con.commit();)把专用线程从t1中移除:t1.remove();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值