Java持久化基础-优化封装JDBC工具类
?
今天对昨天的JDBC连接进行了优化和封装,优化如下:
1、将数据库驱动等连接参数放入属性文件,通过读取属性文件来获取,这样在不更改源码的前提下即可实现对各种关系型数据库的连接,只需修改配置文件即可;
2、将驱动的加载、属性文件的读取等一系列一次性操作放进静态域,这样保证程序只执行一次,提高运行效率;
3、优化封装了查询方法query(),只需传递sql命令和需要返回的对象类型就可获得一个对象集合,在展示层只需遍历集合直接操作对象获取属性值,完全面向对象的方式;
注:对query()的优化和封装大量使用了Java反射,虽然在效率上有损失,但是对于面向对象和前台的操作习惯而言无异于利大于弊。
?
1、JDBCUtil工具类
class="java" name="code">import java.io.File; import java.io.FileInputStream; 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.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; public class JDBCUtil { private static String CLASSNAME; //驱动类 private static String DRIVER; //驱动,填写数据库IP、端口和数据库名 private static String DBUNAME; //数据库用户名 private static String DBPWD; //数据库密码 private static final Properties pro = new Properties(); //属性文件对象 private static final String FILEDIR = "src/conn.properties"; //指定属性文件地址 public static Connection conn = null; public static PreparedStatement ps = null; public static ResultSet rs = null; private List entityList = null; public JDBCUtil(){ this.getConnection(); //初始化连接 } //通过静态代码块读取属性文件和注册数据库驱动,保证只执行一次 static{ try { FileInputStream connFileStream =new FileInputStream(new File(FILEDIR)); pro.load(connFileStream); //读取属性文件 } catch (Exception e) { e.printStackTrace(); } //获取对应值 CLASSNAME = pro.getProperty("CLASSNAME"); DRIVER = pro.getProperty("DRIVER"); DBUNAME = pro.getProperty("DBUNAME"); DBPWD = pro.getProperty("DBUPWD"); try { Class.forName(CLASSNAME); //加载驱动 } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取一个连接对象 */ public void getConnection(){ try { conn = DriverManager.getConnection(DRIVER,DBUNAME,DBPWD); } catch (Exception e) { System.out.println("数据库连接失败!"); e.printStackTrace(); } } /** * 优化后的query,只需传递sql命令和需要返回的对象类型就可获得一个对象集合, * 在展示层只需变量集合直接操作对象获取属性值,完全面向对象的方式 * @param sql —— 查询SQL命令 * @param parms —— 查询条件 * @param classPo —— 需要返回结果集合的类型 * @return —— 返回一个LIST容器装PO对象,前台可直接遍历操作对象 */ public List query( String sql , Object[] parms,Class classPo ){ entityList = new ArrayList(); //Map结构:key:表字段名 value:表字段值 //LIST结果:获取每一行数据, //如:[{PHONE=13441231244, ADDRESS=成都市, SEX=男, YNAME=张三}, {PHONE=13551234123, ADDRESS=成都市, SEX=男, YNAME=李大}] List<Map<String,Object>> resultList = new ArrayList<Map<String,Object>>(); try { ps = conn.prepareStatement(sql); //预编译SQL if( 0!=parms.length ){ for( int i = 0; i<parms.length; i++ ){ ps.setObject(i+1, parms[i]); //循环设置参数 } } rs = ps.executeQuery(); //执行查询操作 //下面开始封装每一行数据放入MAP,并将每行数据放入LIST if( rs != null ){ ResultSetMetaData rsm = rs.getMetaData(); //用于获取结果集中列的类型和属性信息对象 while( rs.next() ){ Map<String,Object> map = new HashMap<String,Object>(); for( int i = 1; i<=rsm.getColumnCount();i++ ){ map.put(rsm.getColumnName(i), rs.getObject(i)); //字段名称——字段值 } resultList.add(map); //将一行的数据放入LIST } } //利用反射来封住数据返回一个指定对象类型的数据集 //LIST结构:[AddressBookPO@9446e4, AddressBookPO@ba5c7a, AddressBookPO@10d593e] entityList = ResultSetToObject.ToObjectList(classPo, resultList); } catch (Exception e) { e.printStackTrace(); } try { this.close(); //关闭所有对象 } catch (Exception e) { e.printStackTrace(); } return entityList; } /** * 增删改操作 * @param sql SQL查询语句 * @param pares 判断条件 * @return */ public int edit( String sql, Object[] pares ){ int hasEffect = 0; try { ps = conn.prepareStatement(sql); //预编译SQL if( 0 != pares.length ){ for( int i = 0; i<pares.length; i++ ){ ps.setObject(i+1, pares[i]); //循环设置参数 } } hasEffect = ps.executeUpdate(); //执行增删改返回影响行数 } catch (SQLException e) { e.printStackTrace(); } try { this.close(); //关闭所有对象 } catch (Exception e) { e.printStackTrace(); } return hasEffect; } /** * 关闭所有对象 * @throws Exception */ public void close() throws Exception{ if( rs != null ){ rs.close(); } if( ps != null ){ ps.close(); } if( conn != null ){ conn.close(); } } }
?
2、conn.properties属性文件格式
?
CLASSNAME=oracle.jdbc.driver.OracleDriver DRIVER=jdbc\:oracle\:thin\:@127.0.0.1\:1521\:ORCL DBUNAME=scott DBUPWD=tiger
?3、封装实体类——ResultSetToObject
?
import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 封装实体类-将制定对象类型数据通过反射赋值初始化对象 * @author Administrator */ public class ResultSetToObject { private static List resultList = null; private static Object o = null; /** * 下面这个方法有个弊端:实体对象的属性必须和数据库对应表的字段名称一样,如USER对象有一个name属性 * 那么其对于数据表TUSER的字段名称也必须是NAME。 * 如果属性名称和字段名称不一致怎么办? * 解决办法:可以将对象的属性和对应数据表的字段名做一个映射,最简单的就是创建一个属性文件以键值对方式存放 * 如:name:USERNAME,age:USERAGE。通过读取属性文件来匹配属性和字段的映射。 * @param c * @param resultSetList * @return * @throws Exception */ public static List ToObjectList( Class c, List<Map<String,Object>> resultSetList ) throws Exception{ resultList = new ArrayList(); Field[] fields = c.getDeclaredFields(); for( Map<String,Object> map : resultSetList ){ o = c.newInstance(); for( Field f:fields ){ String fieldName = f.getName(); String methodName = "set"+fieldName.substring(0,1).toUpperCase() + fieldName.substring(1); Method method = c.getMethod(methodName, new Class[]{f.getType()}); method.invoke(o, map.get(f.getName().toUpperCase())); } resultList.add(o); } return resultList; } }
?4、实体对象——AddressBookPO.java
public class AddressBookPO { public String yname; public String sex; public String phone; public String address; public String getYname() { return yname; } public void setYname(String yname) { this.yname = yname; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
5、测试类
import java.util.ArrayList; public class MyDAO { public static void main(String[] args) { JDBCUtil jdbc = new JDBCUtil(); String sql = "select * from addressbook t where t.address = '成都市'"; ArrayList<AddressBookPO> addressBookList = (ArrayList<AddressBookPO>) jdbc.query(sql, new Object[]{}, AddressBookPO.class); for(AddressBookPO addressBook:addressBookList ){ System.out.println(addressBook.getYname()); //输出满足居住地在成都市的姓名 } } } 运行结果: 张三 李大
?
?6、数据表结构
数据表ADDRESSBOOK
?
prompt PL/SQL Developer import file prompt Created on 2016年5月4日 by Administrator set feedback off set define off prompt Creating ADDRESSBOOK... create table ADDRESSBOOK ( YNAME VARCHAR2(50), SEX VARCHAR2(50), PHONE VARCHAR2(50), ADDRESS VARCHAR2(50) ) tablespace USERS pctfree 10 initrans 1 maxtrans 255 storage ( initial 64K minextents 1 maxextents unlimited ); prompt Disabling triggers for ADDRESSBOOK... alter table ADDRESSBOOK disable all triggers; prompt Loading ADDRESSBOOK... insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('张三', '男', '13441231244', '成都市'); insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('李大', '男', '13551234123', '成都市'); insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('王黎', '女', '13441234211', '广元市'); insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('周提奥', '男', '13665423212', '德阳市'); insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('张凯', '男', '13651234287', '南充市'); insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('孟麥', '男', '13654355433', '西昌市'); insert into ADDRESSBOOK (YNAME, SEX, PHONE, ADDRESS) values ('凤荏熙', '女', '13554345642', '遂宁市'); commit; prompt 7 records loaded prompt Enabling triggers for ADDRESSBOOK... alter table ADDRESSBOOK enable all triggers; set feedback on set define on prompt Done.
?
?
?