嗯,大家肯定是经常使用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 + "]"; } }