嗯,大家肯定是经常使用jdbc完成一些基本的数据库操作,但是每次有没有发现都是差不多同样子的操作呢?为了解决这个问题,下面给出一个基本的jdbc封装。在这个列子中,
采用了单例设计模式,并且很好的控制了线程并发问题。因为数据的操作,每个Connection, Statement, PreparedStatement,ResultSet对象,都可能造成线程并发问题,所以在这个
类里面,全部的对象都是在方法里面定义,这样子,每个方法都拥有自己的运行堆栈,从而在使用单列模式的情况下,也不会有线程并发问题。有时候,在我们刚开始使用jdbc的
时候,会直接把Connetion, Statement, PreparedStatement, ResultSet的对象定义为一个类的属性...那样子很容易造成并发问题。
这个类同样把数据库的配置,放在一个xml文件中,这样子更具有灵活性。在修改xml的情况下,既可以完成数据库的更换。
下面是xml的内容:
url=jdbc:mysql://localhost:3306/
username=itaem
password=imitaem
dbName=gdou_gym
driver=org.gjt.mm.mysql.Driver
下面是封装的JdbcTool工具类:
package net.itaem.tool;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
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.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import net.itaem.po.Field;
/**
* 操作数据库的工具类,这个类负责建立于数据库的连接,负责执行update, insert, delete等基本的数据库操作
* 这个类大家可以直接使用,不需要考虑过多的线程问题
* 这个类写了通用的update, insert, delete等数据库访问方法,具体到特殊的查询,以及含有事务的sql组合
* 大家可以直接写在自己的dao中
* 注意点:调用了getConnection()之后,记得在宿主程序中关闭你所获得的Connection变量
* @author luohong
* @date 2014-03-09
* */
public class JdbcTool {
private static JdbcTool instance = new JdbcTool();
//这些属性不可以修改,只允许直接从 jdbc.cfg.properties读取
private static String dbName; //数据库名字
private static String url; //数据库连接的url
private static String username; //数据库用户名
private static String password; //密码
/**
* 解析数据库的配置文件
* 从配置文件中读取数据库连接的 url, username, password, driver, database name等各种信息
* 以后要修改数据库的链接地址,直接修改配置文件,不要修改该类
* */
static{
try {
Properties properties = new Properties();
InputStream inputStream = JdbcTool.class.getClassLoader().getResourceAsStream("jdbc.cfg.properties");
properties.load(inputStream);
//加载数据库驱动
Class.forName((String)properties.get("driver"));
dbName = (String)properties.getProperty("dbName");
url = (String)properties.getProperty("url");
password = (String)properties.getProperty("password");
username = (String)properties.getProperty("username");
} catch (Exception e) {
throw new RuntimeException("加载mysql驱动失败");
}
}
/**
* 这个类的访问接口,返回这个类的实例,每次访问,返回的都是同一个实例
* */
public static JdbcTool getJdbcToolInstance(){
return instance;
}
/**
* 采用单列设计模式
* */
private JdbcTool(){
}
/**
* 获取数据库连接
* 返回的每个Connection都是 new 出来的唯一Connection
* @return Connection 返回数据库的链接
* @exception RuntimeException 如果获取数据库连接失败,直接抛出运行时异常,调用者不需要捕获该异常
* */
public Connection getConnection(){
try {
Connection conn = DriverManager.getConnection(url + dbName, username, password);
return conn;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("获取数据库连接失败" + ",url:" + url + dbName + ",username:" + username + ", password:" + password);
}
}
/**
* 执行insert, update, delete的sql语句,如果操作影响结果
* @param sql 要执行的sql语句,形式:insert into student(name, number) values (?,?)
* @param args 要执行数据库的sql参数,如果为null,直接执行sql语句
* @return 返回记录修改的数目,如果出现异常,返回 -1
* */
public int executeUpdate(String sql, Object[] args){
Connection conn = getConnection();
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
if(args != null && args.length > 0)
//注入sql ? 的实际内容
for(int i=0; i<args.length; i++)
ps.setObject(i + 1, args[i]);
//如果没有参数,直接执行
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
return -1;
}finally{
//关闭数据库连接
try {
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 批量执行sql语句,调用该方法之间,必须组装好sql语句
* sql语句只能是增加,删除,修改三种类型的语句
* @param sqlArray 要批量执行的sql语句
* @return 返回批量执行后的int数组
* @exception SQLException
* */
public int[] executeUpdateBatch(String[] sqlArray){
Connection conn = getConnection();
Statement statement = null;
try {
statement = conn.createStatement();
for(String sql : sqlArray)
statement.addBatch(sql);
return statement.executeBatch();
} catch (SQLException e) {
e.printStackTrace();
}finally{
//关闭数据库连接
try {
statement.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
/**专门用于插入主键自动增长的数据库表的操作,返回值为自动增长的主键值
* @author 少钦
* @param sql
* @param objs
* @return 插入成功的自动增长的主键,如果没有成功则返回-1
*/
public int add(String sql,Object[] objs){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
JdbcTool tool=JdbcTool.getJdbcToolInstance();
conn = tool.getConnection();
st = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
for (int i = 0; objs != null && i < objs.length; i++) {
st.setObject(i + 1, objs[i]);
}
st.executeUpdate();
rs = st.getGeneratedKeys();
if (rs.next()) {
return rs.getInt(1);
}else
return -1;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("获取数据过程中出现异常" + e);
} finally {
JdbcTool.release(conn,st,rs);
}
}
/**专门用来关闭数据库连接的方法,st可以PreparedStatement(Statement是pt的父类)
* @author 少钦
* @param conn
* @param st PreparedStatement或者Statement
* @param rs
*/
public static void release(Connection conn,Statement st,ResultSet rs){
try{
if(rs!=null){
try{
rs.close();
}catch(SQLException e){
throw e;
}
}
if(st!=null){
try{
st.close();
}catch(SQLException e){
throw e;
}
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
throw e;
}
}
}catch (SQLException e) {
throw new RuntimeException("关闭数据库连接时发生异常!异常信息:"+e);
}
}
public static void main(String[] args) throws Exception{
System.out.println(getTableList("select * from field where field_id>?", new Object[]{10}, new Field()));
}
/**
* 根据sql,返回对象的集合
* 这个方法的使用要求:传入的sql属于预处理的sql
* args可以为null,或者是一个没有内容的Object[]数组
* object是要封装的数据类型,在每次方法返回的时候,必须要转换为你所需要的数据类型,当然,读者可以自己修改为泛型方法,那样子封装更加完美
*
* bug:因为封装过程中,每次只是修改Object的内容,然后添加到集合中,所以这个集合中的所有对象都是同一个对象...不可以使用Set进行转换
*
* @param args sql的参数
* @param object 要封装的类型
*
* @throws SQLException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* */
public static List<Object> getTableList(String sql, Object[] args, Object object) throws SQLException,
IllegalArgumentException, IllegalAccessException, InvocationTargetException{
Connection conn = getJdbcToolInstance().getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = null;
if(args == null || args.length ==0){ //没有参数
rs = ps.executeQuery(); //查询
}else{ //有参数
for(Object obj: args){
ps.setObject(1, obj);
}
rs = ps.executeQuery();
}
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount(); //the column of the table
@SuppressWarnings("rawtypes")
Class clazz = object.getClass();
//获得所有的属性
java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
String[] methodNames = new String[fields.length];
//get all object method
Method[] methods = object.getClass().getDeclaredMethods();
//获得所有的方法名
for(int i=0; i<fields.length; i++){
methodNames[i] = "set" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1);
}
//封装存在的问题:因为这里每次都把参数object加入到集合中,所以这里所有的集合对象都是同一个对象
List<Object> objList = new ArrayList<Object>();
Set<Object> objSet = new HashSet<Object>();
while(rs.next()){
for(int i=0; i<columnCount; i++){
String methodName = methodNames[i];
for(Method method: methods){
if(method.getName().equalsIgnoreCase(methodName)){
//设置对象的属性
method.invoke(object, rs.getObject(rsmd.getColumnName(i+1)));
}
}
objList.add(object);
}
}
//objSet.addAll(objList);
//System.out.println("the set size is " + objSet.size()); //打印的结果为1!!!!
return objList;
}
}
//使用到的Field类对象
package net.itaem.po;
import java.util.HashSet;
import java.util.Set;
/**
*
* @Description:场地po类
* @operate:
* @author sen
* @date 2014-3-11
*/
public class Field implements java.io.Serializable {
// Fields
private int fieldId; //ID
private int fieldTypeId; //对应的场地类型ID
private String fieldNumber; //场地序号 /场地名称
private boolean fieldStatus; //场地的状态 fieldStatus=true为开启状态 fieldStatus=false为关闭状态
private String fieldPic; //场地存放图片路径
private String fieldRemark; //场地备注
private Set fieldParttimes = new HashSet(0); //场地对应的场地时间段
// Constructors
/** default constructor */
public Field() {
}
/** minimal constructor */
public Field(int fieldId) {
this.fieldId = fieldId;
}
/** full constructor */
public Field(int fieldId, int fieldTypeId, String fieldNumber,
boolean fieldStatus, String fieldPic, String fieldRemark,
Set fieldParttimes) {
this.fieldId = fieldId;
this.fieldTypeId = fieldTypeId;
this.fieldNumber = fieldNumber;
this.fieldStatus = fieldStatus;
this.fieldPic = fieldPic;
this.fieldRemark = fieldRemark;
this.fieldParttimes = fieldParttimes;
}
public Field(int fieldId, int fieldTypeId, String fieldNumber,
String fieldPic, String fieldRemark) {
this.fieldId = fieldId;
this.fieldTypeId = fieldTypeId;
this.fieldNumber = fieldNumber;
this.fieldPic = fieldPic;
this.fieldRemark = fieldRemark;
}
public Field(int fieldTypeId, String fieldNumber,
String fieldPic, String fieldRemark) {
this.fieldTypeId = fieldTypeId;
this.fieldNumber = fieldNumber;
this.fieldPic = fieldPic;
this.fieldRemark = fieldRemark;
}
// Property accessors
public int getFieldId() {
return this.fieldId;
}
public void setFieldId(int fieldId) {
this.fieldId = fieldId;
}
public int getFieldTypeId() {
return fieldTypeId;
}
public void setFieldTypeId(int fieldTypeId) {
this.fieldTypeId = fieldTypeId;
}
public String getFieldNumber() {
return this.fieldNumber;
}
public void setFieldNumber(String fieldNumber) {
this.fieldNumber = fieldNumber;
}
public boolean getFieldStatus() {
return this.fieldStatus;
}
public void setFieldStatus(boolean fieldStatus) {
this.fieldStatus = fieldStatus;
}
public String getFieldPic() {
return this.fieldPic;
}
public void setFieldPic(String fieldPic) {
this.fieldPic = fieldPic;
}
public String getFieldRemark() {
return this.fieldRemark;
}
public void setFieldRemark(String fieldRemark) {
this.fieldRemark = fieldRemark;
}
public Set getFieldParttimes() {
return this.fieldParttimes;
}
public void setFieldParttimes(Set fieldParttimes) {
this.fieldParttimes = fieldParttimes;
}
@Override
public String toString() {
return "Field [fieldId=" + fieldId + ", fieldTypeId=" + fieldTypeId
+ ", fieldNumber=" + fieldNumber + ", fieldStatus="
+ fieldStatus + ", fieldPic=" + fieldPic + ", fieldRemark="
+ fieldRemark + ", fieldParttimes=" + fieldParttimes + "]";
}
}