您的位置:首页 > 数据库 > MySQL

支持连接池和结果集缓存的MySQL数据库JDBC通用框架的轻量级封装(二)——向上封装

2015-08-26 13:52 295 查看


“SQL对象”级别的封装方案,即以一条SQL语句的粒度为单位,将其包装成一个DBObject,利用@DBCondition来声明SQL的输入条件、@DBResult来声明查询所返回的结果,Connector类实现对大量JDBC方法的封装,从而实现对DBObject对象进行处理,完成“SQL对象”中所对应的SQL的运行和结果集的返回,其中所有的JDBC方法细节均由Connector代理,框架使用者不需要进行过多关注。

(1)引入DBObject接口及声明

利用声明(Annotation)特性和BDObject接口来规范SQL对象(以一条SQL为单位粒度),实现对SQL对象类的基本定义。

一个极简的BDObject接口代码参考如下:

public interface DBObject {
public String getSQL();
}


创建@DBCondition声明,在继承DBObject接口的SQL对象类中,使用其对get方法进行声明,从而标识所有的SQL中的需要参数赋值内容,@DBCondition声明代码参考如下代码片段:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DBCondition {
int seq();
}


创建@DBResult声明,在继承DBObject接口的SQL对象类中,使用其对set方法进行声明,从而标识结果集返回的列信息,@DBResult声明代码参考如下代码片段:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DBResult {
String type();
int index();
}


通过以上方法,就可以创建BDObject类,从而来定义一个SQL对象,一个典型SQL对象的实现代码参考如下代码片段:

public class LoginDBObject implements DBObject{
private String username = null;
private String[] password = null;

@Override
public String getSQL() {
// TODO Auto-generated method stub
return "SELECT PASSWORD FROM USERS WHERE USERNAME = ?";
}

@DBCondition(seq = 1)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}

public String[] getPassword() {
return password;
}

@DBResult(index = 1, type = "String[]")
public void setPassword(String[] password) {
this.password = password;
}

}


(2)Connector类实现代理

Connector类将用于对上篇文章框架中所提出的经装饰后的相关数据库方法进行封装(当然也可以剥离上篇文章中的相关方法,对纯JDBC对应方法进行封装),为支持一般的使用,至少需提供以下代理方法:

queryExecute(DBObject o)封装 PoolablePreparedStatement相关的查询方法;

noQueryExecute(DBObject o) 封装PoolablePreparedStatement相关的非查询方法;

simpleQueryExecute(DBObject o) 封装 PoolableStatement相关的查询方法;

simpleNoQueryExecute(DBObject o) 封装PoolableStatement相关的非查询方法;

startTransaction()和endTransaction()用于控制事务的范围

queryExecuteInTransaction(DBObject o)和noQueryExecuteInTransaction(DBObject o)用于封装基于事务控制的相关方法。

利用反射机制,通过fillPreparedStatement (DBObject o)自动根据DBObject对象内部的@DBCondition声明来填充SQL中的?参数占位符,参考如下代码片段:

private void fillPreparedStatement(DBObject o) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SQLException{
Class<?> clazz = o.getClass();

for(Method method : clazz.getMethods()){
for(Annotation annotation : method.getAnnotations()){
if(annotation instanceof DBCondition){
String type = method.getReturnType().toString();
int index = ((DBCondition) annotation).seq();
switch(type){
case "int" : {
pstmt.setInt(index, (int)method.invoke(o));
break;
}
case "long" : {
pstmt.setLong(index, (long)method.invoke(o));
break;
}
case "float" : {
pstmt.setFloat(index, (float)method.invoke(o));
break;
}
case "double" : {
pstmt.setDouble(index, (double)method.invoke(o));
break;
}
default : {
pstmt.setString(index, (String)method.invoke(o));
break;
}
}
}
}
}
}


通过fillDBObject (DBObject o)自动根据DBObject对象内部的@ DBResult声明来明对结果集进行填充,反射应用中利用DBResultMethod类来保存每一个结果集列所对应的反射方法、数据和类型,参考如下代码片段:

import java.lang.reflect.Method;
public class DBResultMethod {
Method method = null;
Object object = null;
String type = null;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}


fillDBObject (DBObject o)的构建参考如下代码片段:

private void fillDBObject(DBObject o){
if(rs != null){
Class<?> clazz = o.getClass();
try {
rs.last();
int rownum = rs.getRow();
if(rownum > 0){

for(Method method : clazz.getMethods()){
for(Annotation annotation : method.getAnnotations()){
if(annotation instanceof DBResult){
Object[] columns = null;
DBResultMethod dbrm = new DBResultMethod();
switch(((DBResult) annotation).type()){
case "Integer[]" : {
columns = new Integer[rownum];
dbrm.setType("Integer[]");
break;
}
case "Long[]" : {
columns = new Long[rownum];
dbrm.setType("Long[]");
break;
}
case "Float[]" : {
columns = new Float[rownum];
dbrm.setType("Float[]");
break;
}
case "Double[]" : {
columns = new Double[rownum];
dbrm.setType("Double[]");
break;
}
case "Date[]" : {
columns = new Date[rownum];
dbrm.setType("Date[]");
break;
}
case "Timestamp[]" : {
columns = new Timestamp[rownum];
dbrm.setType("Timestamp[]");
break;
}
default : {
columns = new String[rownum];
dbrm.setType("String[]");
break;
}
}

dbrm.setMethod(method);
dbrm.setObject(columns);
columnsMap.put(((DBResult) annotation).index(), dbrm);
}
}
}

for(int i = 0; i < rownum; i++){
if(i != 0){
rs.next();
} else {
rs.first();
}
for (int j = 0; j < columnsMap.size(); j++) {
DBResultMethod dbrm = columnsMap.get(j + 1);

switch (dbrm.getType()) {
case "Integer[]": {
Integer[] array = (Integer[]) dbrm.getObject();
array[i] = rs.getInt(j + 1);
break;
}
case "Long[]": {
Long[] array = (Long[]) dbrm.getObject();
array[i] = rs.getLong(j + 1);
break;
}
case "Float[]": {
Float[] array = (Float[]) dbrm.getObject();
array[i] = rs.getFloat(j + 1);
break;
}
case "Double[]": {
Double[] array = (Double[]) dbrm.getObject();
array[i] = rs.getDouble(j + 1);
break;
}
case "Date[]": {
Date[] array = (Date[]) dbrm.getObject();
array[i] = rs.getDate(j + 1);
break;
}
case "Timestamp[]": {
Timestamp[] array = (Timestamp[]) dbrm.getObject();
array[i] = rs.getTimestamp(j + 1);
break;
}
default: {
String[] array = (String[]) dbrm.getObject();
array[i] = rs.getString(j + 1);
break;
}
}

}
}

for(int j = 0; j < columnsMap.size(); j++){
DBResultMethod dbrm = columnsMap.get(j + 1);
Method method = dbrm.getMethod();
try {
method.invoke(o, dbrm.getObject());
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


利用fillPreparedStatement 和fillDBObject方法的实现,我们就可以轻易构建出我们对Connector类在需求中所描述的各类封装要求,Connector类的一个全貌代码如下:

public class Connector {

private static int MAX_COLUMN_NUM = 1024;

private PoolableConnection connection = null;
PoolablePreparedStatement pstmt = null;
PoolableStatement stmt = null;
ResultSet rs = null;
//用于存储返回结果及赋值方法
private Map<Integer, DBResultMethod> columnsMap = new HashMap<Integer, DBResultMethod>(MAX_COLUMN_NUM);
/**
* 基于DBObject对象的查询方法
* @param o
* @throws Exception
*/
public void queryExecute(DBObject o) throws Exception{
try{
connection();
query(o);
} finally {
shutdown();
}

}

/**
* 基于DBObject对象的非查询方法
* @param o
*/
public void noQueryExecute(DBObject o) throws Exception{
try{
connection();
noQuery(o);
} finally {
shutdown();
}

}

/**
* 直接通过SQL语句的查询方法
* @param o
*/
public void simpleQueryExecute(DBObject o) throws Exception{

try{
connection();
simpleQuery(o);
} finally {
shutdown();
}

}

/**
* 直接通过SQL语句的非查询方法
* @param o
*/
public void simpleNoQueryExecute(DBObject o) throws Exception{
try{
connection();
simpleNoQuery(o);

} finally{
shutdown();
}
}

public void startTransaction() throws Exception{
connection();
connection.setAutoCommit(false);
}

public void queryExecuteInTransaction(DBObject o) throws SQLException{
query(o);
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

public void noQueryExecuteInTransaction(DBObject o) throws SQLException{
noQuery(o);
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

public void endTransaction() throws SQLException{
connection.commit();
connection.setAutoCommit(true);
shutdown();
}

/**
* 数据库连接方法
* @throws Exception
*/
private void connection() throws Exception {

connection = new PoolableConnection();

}

/**
* 关闭数据库连接方法中相关对象的方法
*/
private void shutdown() {
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

/**
* 数据库查询方法
* @param o
* @return
* @throws SQLException
*/
private ResultSet query(DBObject o) throws SQLException {

pstmt =  connection.prepareStatement(o.getSQL(), ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
try {
fillPreparedStatement(o);
rs = pstmt.executeQuery();

fillDBObject(o);

} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return rs;
}

/**
* 数据库非查询方法
* @param o
* @throws SQLException
*/
private void noQuery(DBObject o) throws SQLException {

pstmt =  connection.prepareStatement(o.getSQL());
try {
fillPreparedStatement(o);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pstmt.execute();
if(!connection.getAutoCommit()){
connection.commit();
}

}

/**
* 使用拼接SQL方式的数据库非查询方法
* @param o
*/
private void simpleNoQuery(DBObject o){

try {
stmt = connection.createStatement();
stmt.execute(o.getSQL());
if(!connection.getAutoCommit()){
connection.commit();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

/**
* 使用拼接SQL方式的数据库查询方法
* @param o
*/
private void simpleQuery(DBObject o){
try {
stmt = connection.createStatement();
rs = stmt.executeQuery(o.getSQL());
fillDBObject(o);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}


(3)使用封装方法

封装完成后,我们可以直接使用“SQL对象”和封装方法替代原来对于框架使用的代码片段:

Connector connector = new Connector();
DemoDBObject o = new DemoDBObject();
o.setConditionName("%xrez%");
try {
connector.queryExecute(o);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i = 0; i < o.getResultName().length; i++){
System.out.println(o.getResultName()[i]);
}
//查询已经缓存的结果集
o = new DemoDBObject();
o.setConditionName("%xrez%");
try {
connector.queryExecute(o);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i = 0; i < o.getResultName().length; i++){
System.out.println(o.getResultName()[i]);
}


可以看到利用对框架进一步向上封装我们约定了一个以SQL为基本单位的数据库对象,通过对该对象的定义以及一系列封装方法的设计和实现,可以使得用户在使用框架时不需要再过多浪费精力在JDBC数据库方法操作的细节上,这些细节完全代理给框架内部实现,用户只需要关注业务逻辑,另外的好处是,可以再次增强封装内部方法结合结果集缓存的细节性操控,使得用户可以透明的,更加有效的利用框架提升性能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: