一开始学习Oracle JDBC连接的时候一般都是先做一个基础类获取数据库的连接,在用Dao层的类去工作,执行SQL语句。
但是没做一个功能就要写一个执行方法,这样很麻烦,代码也不够优化。我最近参考了一些资料,把连接数据库的基础类和Dao层的方法一起封装到一个类,用到的话直接调用,实现代码优化。
先做好数据库驱动和连接及资源关闭的代码
static {
// 加载驱动
try {
// 读取文件
Properties prop = new Properties();
prop.load(new FileInputStream(new File("config/db.properties")));
// 获取属性
driverName = prop.getProperty("driverName");
url = prop.getProperty("url");
userName = prop.getProperty("userName");
passWord = prop.getProperty("passWord");
// 动态加载驱动 API解释:返回与给定字符串名称的类或接口相关联的 类对象。
Class.forName(driverName);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn = null;
try {
//获取连接数据库的方法DriverManager加载启动管理
conn = DriverManager.getConnection(url, userName, passWord);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
// 提供释放资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
下面是没有封装的Dao层方法 增加数据的方法
public boolean registerAccounts(String users, String passWord) {
Connection conn=null;
PreparedStatement stmt=null;
boolean tmp=false;
try {
conn=getConnection();
stmt=conn.prepareStatement(REGISTER_USER_AND_PASSWORD);
stmt.setString(1,users);
stmt.setString(2,passWord);
stmt.executeUpdate();
conn.commit();
tmp=true;
return tmp;
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}finally {
close(conn, stmt, null);
}
return false;
}
这个封装好的方法可以实现上面的功能,但更加优化
//封装所有dao的更新方法
public boolean actionUpdate(String sql,List<Object> params)
{
//把依赖的属性放到外面 有利于代码有执行程序
Connection conn=null;
PreparedStatement stmt=null;
int result=0;
try {
//获取连接
conn=getConnection();
//通过预便宜存储sql语句的参数
stmt=conn.prepareStatement(sql);
//如何从List集合中获取参数来给stmt中的sql语句中的问号赋值呢?
//params通过for循环逐个放入stmt预编译里面
if(params!=null)
{
for(int i=0;i<params.size();i++)
{
//这里Object是通用的可以变的类型(例如:String,int,char。。。)
//i+1索引?:从0开始的。
//params.get(i)获取那个值,看构造方法的放入位置,(重点注意(连构造方法的列名都要一样))
stmt.setObject(i+1, params.get(i));
}
}
result=stmt.executeUpdate();
}catch(Exception e)
{
e.printStackTrace();
}finally
{
close(conn,stmt,null);
}
//三元运算符
return result>0?true:false;
}
使用封装好的代码
/**
* 增加书籍
*/
@Override
public boolean insertBook(Book book) {
List<Object> params=new ArrayList<Object>();
params.add(book.getBook_name());
return this.actionUpdate(INSERT_BOOK, params);
}
我个人感觉查询的封装方法有点难写,涉及到String和工具类,对象,和反射的知识例如:
//封装所有dao的查询操作
//泛型方法,语法:public <T> List<T> actionQuery(),,,返回数据前声明了一个泛型
public <T> List<T> actionQuery(String sql,List<Object> params,Class<T> cls)
{
Connection conn=null;
PreparedStatement stmt=null;
ResultSet rs=null;
//<T>泛型的集合用来存放各种类型的数据
List<T> list=new ArrayList<T>();
try {
//1.把工具对象初始化
conn=getConnection();
stmt=conn.prepareStatement(sql);
//2.给sql语句中的占位符赋值
if(params!=null)
{
for(int i=0;i<params.size();i++)
{
//这里Object是通用的可以变的类型(例如:String,int,char。。。)
//i+1索引?:从0开始的。
//params.get(i)获取那个值,看构造方法的放入位置,(重点注意(连构造方法的列名都要一样))
stmt.setObject(i+1, params.get(i));
}
}
rs=stmt.executeQuery();
//拿到元数据 ResultSetMetaData可用于获取有关ResultSet对象中列的类型和属性的信息的对象
ResultSetMetaData rm=rs.getMetaData();//getMetaData用于检索此 ResultSet对象的列的数量,类型和属性。
int columnCount=rm.getColumnCount();//获取列数
//2.把结果集中的数据转移到list集合中
while(rs.next())//结果集中有多少行数据就循环多少次,7次
{
//2.1 这里需要实例化一个对象<T>泛型,以接收每行数据 cls的值: Book.class
T obj=cls.newInstance();//new Book(); newInstance创建由此类对象表示的类的新实例。该类被实例化为一个具有空参数列表的new表达式。 如果类尚未初始化,则初始化该类。
//2.2如何给对象的各个属性赋值呢?
//2.3有多少列那么就循环多少次 4次
for(int i=0;i<columnCount;i++)
{
String columnName=rm.getColumnName(i+1);//i+1是索引,从1开始 BOOK_ID,这里的列名和数据库列名是一样的
//获取列名==获取属性名==就可以获取set对应的方法名==就可以拿到Method对象==然后调用给属性赋值
String fieldName=columnName.toLowerCase();// 获取到的列名,全转小写 book_id? 原因:数据库默认传送过来的列名进行统一
//拼出这个名字:setBook_id
//fieldName.substring(0,1).toUpperCase():拿到名字中的首字母然后转成大写,结果是B
//replaceFirst(1,2)用给定的2替换1,这个方法就是后面的值替换第一个值
//substring(0,1)截取第一个字符,并变成大写
String replaceName=fieldName.replaceFirst(fieldName, fieldName.substring(0,1).toUpperCase());
System.out.println("replaceName:"+replaceName);
//这下面的意思是set+B+ook_id
String methodName="set"+replaceName+fieldName.substring(1);
System.out.println("methodName:"+methodName);
//获取Method对象
//获取字段对应的数据类型
//class.getDeclaredField返回一个Field对象,它反映此表示的类或接口的指定已声明字段类对象。 参数是一个String ,它指定了所需字段的简单名称。
//这里用到了反射
Class<?> type=cls.getDeclaredField(fieldName).getType();
//获取方法对象时,加了一个类型参数,这样更准确地定位一个方法 第一个参数是过去到的参数名,第二个是获取到的参数类型
//Method 方法提供有关类和接口上单一方法的信息和访问权限。 反映的方法可以是类方法或实例方法(包括抽象方法)。
//用Method去接收 cls类型的字段名和类型 也就是一个能实际实用的方法了
Method method=cls.getDeclaredMethod(methodName,type);
//method.invoke(obj, rs.getObject(i+1));这行代码不要,下面有调用
//根据数据类型赋值
/*isAssignableFrom确定由此类对象表示的类或接口是否与由指定的Class 类表示的类或接口相同或是超类或类接口。 如果是,则返回true ; 否则返回false
如果此类对象表示基本类型,则如果指定的类参数正好是类对象,则此方法返回true ; 否则返回false 。 */
if(type.isAssignableFrom(String.class))
{
//在具有指定参数的方法对象上调用此方法对象表示的基础方法。
//obj - 从中调用底层方法的对象(简单的说就是调用谁的方法用谁的对象) rs - 用于方法调用的参数
method.invoke(obj, rs.getString(i+1));
}else if(type.isAssignableFrom(int.class)||type.isAssignableFrom(Integer.class))
{
method.invoke(obj, rs.getInt(i+1));
}else if(type.isAssignableFrom(boolean.class)||type.isAssignableFrom(Boolean.class))
{
method.invoke(obj, rs.getBoolean(i+1));
}else if(type.isAssignableFrom(Date.class))
{
method.invoke(obj, rs.getDate(i+1));
}
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();// TODO: handle exception
}finally {
close(conn,stmt,rs);
}
return list;
}
感觉查询封装类比较难理解,多写几次就好多。