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.
?
?
?