介绍
简介
我刚学JDBC的时候,在DAO层中获取数据是这样写的:
但如果使用过DButils包或者Spring JDBCTemplate,就知道,在JavaBean里,代码可以这样写:
所以相比之下,第一种方法看起来就像原始人一样.....
接下来我将介绍一下,如何自己写一个简单的类来模拟这个功能。Ps:这里我用到了数据库连接池。
准备
JDBC驱动:mysql-connector-java-xxx.jar
C3P0数据库连接池:c3p0-xxx.jar
实现
先创建一个名为JDBCTools的类
连接
创建数据库连接池
首先,你需要一个配置文档,取名为c3p0-config
,内容写法如下:
root
自己写你的密码去
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/你的Database
5
20
5
50
30
5
由于你这个类中所有的操作都是基于实例化了这个数据库连接池之后才能做事的,所以:
1.这个类需要有一个数据源DataSource作为成员变量。
2.需要先使用静态代码块来先实例化。(关于为什么不使用构造函数呢,因为这个工具类的所有方法都将被写为static方法,本类不会被实例化,所以不需要写构造函数)
实现如下:
private static DataSource dataSource = null;
static{
dataSource = new ComboPooledDataSource("写你刚起的那个名字");
}
获取连接
有了一个DataSource,获取连接这件事情就变得很简单了:在数据库连接池中之前已经创建好了很多个Connection了,现在你只需要找它要一个就对了。
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
释放
Java7里的自动释放
Java7里面增加了个语法糖:try-with-resources。用法是在try后面加上括号,括号里面new一个对象(但是要求实现java.lang.AutoCloseable接口),编译器在这个对象使用完后自动检测null然后关闭。不过鉴于我们这里的有些new操作是封装了的,所以这样似乎不太方便,所以,可以利用:Connection,Statement,resultSet都实现java.lang.AutoCloseable接口的特性来写一个通用的关闭方法:
public void close(AutoCloseable auto)
{
if(auto!=null)
{
try {
auto.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
传统的方法
不过我个人写的是另外一种方法:一下把Connection,Statement,resultSet都关了的方法。
public static void releaseDB(ResultSet resultSet, Statement statement,
Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Connection,Statement,resultSet这三个是在各种操作中最常见的,但是如果你只需要用到两个类,你用null来占位置就行了。
==特别说明==:在使用了数据库连接池之后,Connection的close()操作就不再是真正意义上的关闭连接了,而是把这个连接放回数据库连接池中,再次变成空闲状态。
更新
在执行增,删,改这类操作的时候,只用执行SQL语句即可,不需要从数据库中的得到什么,所以我们需要的只是
1.统一地补全PreparedStatement语句
2.执行
在补全语句的时候,为了使得代码的耦合性降低,给几个参数就补全几个,所以这里使用可变参数 Object ... args
来。
(这里多说一句,不管SQL语句有没有占位符?
最好用PreparedStatement,因为Statement在执行的时候有可能被SQL注入)
public static void executeSql(String sql, Object ... args)
{
Connection conn = null;
try {
conn = getConnection();
} catch (Exception e1) {
e1.printStackTrace();
}
PreparedStatement ps = null;
try
{
ps = conn.prepareStatement(sql);
//逐个填写
for(int i=0;i
查询
与更新不同的是,查询会返回某个值。
获取一组对象
由于获取的对象的类可能是各种各样的,这里需要用到泛型和反射的知识,使得这个方法可以对所有的类都通用。
此外还需JDBC结果集的元数据(ResultSetMetaData)。
所以,
1.同上面那个更新方法一样的执行操作,得到resultSet
2.利用resultSet得到元数据(让你知道哪行的哪个值对应的属性名字是什么)
3.创建一个目标类的List,准备接受内容。
4.把每行的值和名字都依次放到一个HashMap
5.这样你会得到一个HashMap的数组,数组里每个HashMap代表的是实际每个对象的信息结果
6.对这个数组,进行循环操作:
(1)先利用反射实例化一个对象
(2)通过HashMap中储存的名字和值来给这个对象赋值
(3)把赋值好了的对象加入到第3步中的List中
7.返出List即可
对于第6步中的(2),我们得到的“名字”实际上是数据库的表里面的首行的名字,而不一定和JavaBean中的变量名字一样,这样会导致赋值失败。例如数据库中的是user_id,而JavaBean中是userId。
所以我们可以
1.把表头的属性名字取的和JavaBean的一样(但是这个方法不推荐,虽然我就是这样做的)
2.利用AS语法:SELECT user_id AS userId,……
而利用名字给对象属性赋值也有两种方法
1.反射赋值,就是我下面代码些的那种。
2.懒人使用commons-beanutils
工具类,点此下载。
还需要commons-logging.jar
,点此下载。
然后,使用方法如下:
BeanUtils.setProperty(对象的名字, "属性的名字", 属性的值);
下面是正式代码:
public static List executeQuery(Class clazz, String sql, Object ... args)
{
Connection conn = null;
try {
conn = getConnection();
} catch (Exception e1) {
e1.printStackTrace();
}
PreparedStatement ps = null;
ResultSet rs = null;
List list = new ArrayList();
try
{
ps = conn.prepareStatement(sql);
//填写语句
for(int i=0;i map = new HashMap();
for(int i = 1;i<=rsmd.getColumnCount();i++)
{
map.put(rsmd.getColumnLabel(i), rs.getObject(i));
}
//利用反射创建一个实例
T entity = (T) clazz.newInstance();
//利用反射赋值
for(Map.Entry entry : map.entrySet())
{
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
//反射赋值,如果你用了BeanUtils就替换下面三句
Field f = entity.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(entity, fieldValue);
}
list.add(entity);
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
releaseDB(rs, ps, conn);
}
return list;
}
不过这里有个小小的Bug。当你只需要返回一个对象的时候,这个方法返回给你的是个只有一个对象的List,所以嘛,就手动加点代码啦,例如:
public Article ViewArticle(int id) {
String sql = "SELECT * FROM ARTICLE WHERE ID = ?";
List list = JDBCTools.executeQuery(User.class, sql, id);
//手动获取第一个对象XD
User user = list.get(0);
return user;
}
获取某个值
本质上和上面那个一样,但是返回的就是个值,所以简单多了。
public static int executeGetSum(String sql, Object ... args)
{
Connection conn = null;
try {
conn = getConnection();
} catch (Exception e1) {
e1.printStackTrace();
}
PreparedStatement ps = null;
ResultSet rs = null;
int value = 0;
try
{
ps = conn.prepareStatement(sql);
//填写语句
for(int i=0;i
我的JDBCTools类的完整代码
dbq我还没写注释文档......
package com.dao;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* JDBC 的工具类
*
* 其中包含: 获取数据库连接, 关闭数据库资源等方法.
*/
public class JDBCTools {
private static DataSource dataSource = null;
//数据库连接池应只被初始化一次. 自动创建了一堆Conn然后来管理
//然后这里的conn.close的含义 就变为了释放 而不是关闭!
static{
dataSource = new ComboPooledDataSource("LehrC3P0");
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
public static void releaseDB(ResultSet resultSet, Statement statement,
Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//执行一条sql,没有返回值
@Test
public static void executeSql(String sql, Object ... args)
{
Connection conn = null;
try {
conn = getConnection();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
PreparedStatement ps = null;
try
{
ps = conn.prepareStatement(sql);
//填写语句
for(int i=0;i
* @param sql
* @param args
* @return
*/
@SuppressWarnings({ "rawtypes" })
public static List executeQuery(Class clazz, String sql, Object ... args)
{
Connection conn = null;
try {
conn = getConnection();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
PreparedStatement ps = null;
ResultSet rs = null;
List list = new ArrayList();
try
{
ps = conn.prepareCall(sql);
//填写语句
for(int i=0;i map = new HashMap();
for(int i = 1;i<=rsmd.getColumnCount();i++)
{
map.put(rsmd.getColumnLabel(i), rs.getObject(i));
}
//利用反射创建一个实例
@SuppressWarnings("unchecked")
T entity = (T) clazz.newInstance();
//利用反射赋值
for(Map.Entry entry : map.entrySet())
{
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
//直接对属性操作!但是这里时间类和Long那个有点小问题
Field f = entity.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(entity, fieldValue);
//其实这里也可以导入Beanutils包来快速操作的
}
list.add(entity);
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
releaseDB(rs, ps, conn);
}
return list;
}
//不用加AS NUM !!!
public static int executeGetSum(String sql, Object ... args)
{
Connection conn = null;
try {
conn = getConnection();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
PreparedStatement ps = null;
ResultSet rs = null;
int value = 0;
try
{
ps = conn.prepareStatement(sql);
//填写语句
for(int i=0;i