09年和朋友,寫了個仿SSH的框架例子,將其三塊代碼集成(仿structs、仿spring、仿hibernate),並作了個小網站(JSTL+JSP+(仿)SSH),採起這個框架,感受挺好用的。
主要目的是爲了提升對框架的認識,故沒有真正的SSH複雜,意在道明其因由,我的認爲,對於企業項目開發可以理解框架原理便可。
因仿hibernate的這塊代碼非本人所寫的,難免的有點點遺憾,前些日子無聊,又看到另個朋友採用註解寫了個小框架(針對android系統的),也想採用註解寫個試試。
通過幾天的折騰,也總算是出爐了。時間倉租,應該會有很多瑕疵,之後有時間,慢慢優化咯。
優勢:
1. 以對象方式操做數據庫,使用簡單方便.
2. 所依賴的jar包少,僅須要數據庫驅動的jar.
3. 以上兩個優勢,對於業務不復雜的小型系統來講,蠻方便的.
DMIS-JHibernate關鍵代碼
DMIS-JHibernate.jar DMIS-JHibernate_src.zip見附件
=====================================
先來看下註解的含義,見註釋
/*
* 元註解@Target,@Retention,@Documented,@Inherited
*
* @Target 表示該註解用於什麼地方,可能的 ElemenetType 參數包括:
* ElemenetType.CONSTRUCTOR 構造器聲明
* ElemenetType.FIELD 域聲明(包括 enum 實例)
* ElemenetType.LOCAL_VARIABLE 局部變量聲明
* ElemenetType.METHOD 方法聲明
* ElemenetType.PACKAGE 包聲明
* ElemenetType.PARAMETER 參數聲明
* ElemenetType.TYPE 類,接口(包括註解類型)或enum聲明
*
* @Retention 表示在什麼級別保存該註解信息。可選的 RetentionPolicy 參數包括:
* RetentionPolicy.SOURCE 註解將被編譯器丟棄
* RetentionPolicy.CLASS 註解在class文件中可用,但會被VM丟棄
* RetentionPolicy.RUNTIME VM將在運行期也保留註釋,所以能夠經過反射機制讀取註解的信息。
*
* @Documented 將此註解包含在 javadoc 中
*
* @Inherited 容許子類繼承父類中的註解
*
* @author ypf
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Table {
/**
* 表名
*
* @return
*/
public abstract String name();
}
=====================================
/**
* 註解表 標識是否爲主鍵
* @author ypf
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public abstract @interface Id {
}
=====================================
/**
* 註解表 標識所映射的數據庫字段
*
* @author ypf
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public abstract @interface Column {
/**
* 列名
* @return String
*/
public abstract String name();
/**
* 類型
* @return String
*/
public abstract String type() default "";
/**
* 類型
* @return int
*/
public abstract int length() default 0;
}
=====================================
/**
* 數據庫輔助類
* @author ypf
*/
public class DBHelper {
Logger logger = Logger.getLogger(DBHelper.class.getName());
private String driver;
private String url;
private String dbname;
private String dbpass;
public DBHelper(String driver, String url, String dbname, String dbpass) {
super();
this.driver = driver;
this.url = url;
this.dbname = dbname;
this.dbpass = dbpass;
}
public Connection getConn() throws ClassNotFoundException, SQLException {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, dbname, dbpass);
return conn;
}
/**
* 釋放資源
*
* @param conn-鏈接對象
* @param pstmt-預編譯
* @param rs-結果集
*/
public void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 執行增、刪、改
* @param sql-語句
* @param params-數組型參數
* @return int 受影響的行數
* @throws SQLException
* @throws ClassNotFoundException
*/
public int executeUpdate(String sql, Object... params) throws SQLException, ClassNotFoundException {
Connection connection = null;
PreparedStatement ps = null;
int num = 0;
// 處理SQL,執行SQL
try {
connection = getConn(); // 獲得數據庫鏈接
ps = connection.prepareStatement(sql); // 預處理SQL語句
buildPreparedStatement(ps, params);
logger.info("Oper: " + sql);
num = ps.executeUpdate(); // 執行SQL語句
}catch (SQLException e) {
throw e;
}catch (ClassNotFoundException e) {
throw e;
}finally {
closeAll(connection, ps, null);
}
return num;
}
/**
* 爲PreparedStatement對象填充參數
* @param ps 預編譯句子
* @param param 動態參數
* @throws SQLException
*/
public void buildPreparedStatement(PreparedStatement ps, Object... params) throws SQLException {
// 設置參數
for (int i = 0; i < params.length; i++) {
// 將Date轉換成java.sql.Date
if (params[i] instanceof Date) {
Date d = (Date) params[i];
ps.setObject(i + 1, new Timestamp(d.getTime()));
} else {
ps.setObject(i + 1, params[i]);
}
}
}
/**
* 執行查詢操做
* @param sql sql語句
* @param param 動態參數
* @return RowSet 結果集,可直接使用
*/
public RowSet executeQuery(String sql, Object... params) throws SQLException, ClassNotFoundException {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
RowSet rowset = null;
try {
connection = this.getConn();
ps = connection.prepareStatement(sql);
buildPreparedStatement(ps, params);
logger.info("Oper: " + sql);
rs = ps.executeQuery();
rowset = populate(rs);
} catch (SQLException e) {
throw e;
} catch (ClassNotFoundException e) {
throw e;
} finally {
closeAll(connection, ps, rs);
}
return rowset;
}
/**
* 將ResultSet轉換爲RowSet對象
* @param rs 數據集
* @return RowSet
*/
public RowSet populate(ResultSet rs) throws SQLException {
CachedRowSetImpl crs = new CachedRowSetImpl();
crs.populate(rs);
return crs;
}
/**
* 從RowSet中獲取列頭
* @param rs 數據集
* @return List
*/
public List getRowSetColName(RowSet rs) throws SQLException {
ArrayList collist = new ArrayList();
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
collist.add(rs.getMetaData().getColumnName(i + 1).toUpperCase());
}
return collist;
}
}
=====================================
/**
* 用於建立表的輔助類
*
* @author ypf
*/
public class TableHelper {
private Logger log = Logger.getLogger(TableHelper.class.getName());
private Session session;
public TableHelper(Session session) {
this.session = session;
}
/**
* 建立表
* @param <T>
* @param clazz
*/
public <T> void createTable(Class<T> clazz) {
String tableName = "";
if (clazz.isAnnotationPresent(Table.class)) {
Table table = (Table) clazz.getAnnotation(Table.class);
tableName = table.name();
} else {
return;
}
StringBuilder sb = new StringBuilder();
// 用來判斷表是否存在,若是存在則不執行改操做
sb.append("CREATE TABLE ").append(tableName).append(" (");
List<Field> allFields = ClassUtil.joinFields(clazz);
for (Field field : allFields) {
if (!field.isAnnotationPresent(Column.class)) {
continue;
}
Column column = (Column) field.getAnnotation(Column.class);
String columnType = "";
// 判斷column的type字段是否爲空,若是爲空則根據字段的值將其轉換爲對應的數據庫類型
if (column.type().equals(""))
columnType = getColumnType(field.getType());
else {
columnType = getColumnType(column.type());
}
sb.append(column.name() + " " + columnType);
if (column.length() != 0) {
sb.append("(" + column.length() + ")");
}
// 添加主鍵
if (field.isAnnotationPresent(Id.class)) {
sb.append(" PRIMARY KEY");
}
sb.append(", ");
}
sb.delete(sb.length() - 2, sb.length() - 1);
sb.append(")");
String sql = sb.toString();
String checkSql = "SELECT 1 FROM USER_TABLES WHERE TABLE_NAME=?";
String dropSql = "DROP TABLE " + tableName.toUpperCase();
try {
RowSet rs = session.executeQuery(checkSql, tableName.toUpperCase());
// 若是存在則先刪除
if (rs.next()) {
log.info("表[" + tableName.toUpperCase() + "]已經存在!");
// session.executeUpdate(dropSql);
// session.executeUpdate(sql);
} else {
session.executeUpdate(sql);
log.info("表[" + tableName.toUpperCase() + "]建立成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//根據字段類型返回數據庫對應的列,之後須要擴展優化,一遍支持多中數據庫,目前只支持oracle
private String getColumnType(Class<?> fieldType) {
if (String.class == fieldType) {
return "VARCHAR";
}
if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) {
return "NUMBER";
}
if ((Long.TYPE == fieldType) || (Long.class == fieldType)) {
return "LONG";
}
if ((Float.TYPE == fieldType) || (Float.class == fieldType)) {
return "NUMBER";
}
if ((Double.TYPE == fieldType) || (Double.class == fieldType)) {
return "NUMBER";
}
if (Blob.class == fieldType) {
return "BLOB";
}
if (Date.class == fieldType) {
return "DATE";
}
return "VARCHAR";
}
/**
* 根據傳入的類型返回對應的數據庫字段類型 若是找不到匹配的則直接用其自己做爲數據庫列類型(type不容許爲空)
* 備註:這裏之後會擴展(根據不用的數據庫建立不用的字段)
* @param type
* @return String
*/
private String getColumnType(String type) {
if (ColumnType.INTEGER.equals(type)) {
return "NUMBER";
}
if (ColumnType.STRING.equals(type)) {
return "VARCHAR";
}
if (ColumnType.LONG.equals(type)) {
return "LONG";
}
return type;
}
}
=====================================
/**
* 對外公開的數據庫訪問類
*
* @author ypf
*/
public interface Session {
/**
* 類加載
*/
public abstract void loadContext(ClassLoad c);
/**
* 添加
*/
public abstract Serializable insert(Object entity) throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException, Exception;
/**
* 刪除
*/
public abstract Serializable delete(Object entity) throws SQLException, ClassNotFoundException;
/**
* 修改
*/
public abstract Serializable update(Object entity) throws SQLException, ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException;
/**
* 根據id獲取對象
*/
public abstract Object getResource(Serializable id, Class clazz) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException;
/**
* 查詢全部
*/
public abstract List query(Class clazz) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException;
/**
* 封裝執行sql代碼.
*/
public int executeUpdate(String sql, Object... params) throws SQLException, ClassNotFoundException;
/**
* 經過sql語句,進行查詢操做
* @param sql sql預警
* @param params 參數
* @return RowSet
*/
public RowSet executeQuery(String sql, Object... params) throws SQLException, ClassNotFoundException;
/**
* 經過sql語句,進行增、刪、改操做
* @param sql sql預警
* @param params 參數
* @return RowSet
*/
public Object executeQueryUnique(String sql, Object... params) throws SQLException, ClassNotFoundException;
//未完待續...
}
=====================================
/**
* 提供CRUD操做的實現類
* @author ypf
*/
public class SessionImpl implements Session {
private Logger log = Logger.getLogger(SessionImpl.class.getName());
DBHelper dbHelper; // 數據庫訪問類
Class clazz; // 當前處理對象
String tableName; // 代表
List<Field> allFields; // 全部字段
String keyName; // 主鍵
public void loadContext(ClassLoad c) {
c.execute();
}
/**
* 構建列信息,如:name,type
* @return String
*/
private String buildStrColumn() {
String step = "";
for (int i = 0; i < allFields.size(); i++) {
Column column = (Column) allFields.get(i).getAnnotation(Column.class);
step += column.name() + ",";
}
if (step.length() > 0)
step = step.substring(0, step.length() - 1);
}else {
step = "*";
}
return step;
}
/**
* 構建參數信息,如:?,?
* @return String
*/
private String buildValColumn() {
String step = "";
for (int i = 0; i < allFields.size(); i++) {
step += "?,";
}
if (step.length() > 0)
step = step.substring(0, step.length() - 1);
return step;
}
/**
* 初始化
*
* @param clazz
*/
private void initProperites(Class clazz) {
this.clazz = clazz;
if (this.clazz.isAnnotationPresent(Table.class)) {
Table table = (Table) clazz.getAnnotation(Table.class);
this.tableName = table.name();
}
this.allFields = ClassUtil.joinFields(clazz);
for (Field field : allFields) {
if (!field.isAnnotationPresent(Column.class)) {
continue;
}
Column column = (Column) field.getAnnotation(Column.class);
if (field.isAnnotationPresent(Id.class)) {
this.keyName = field.getName();
}
}
log.info("clazz:" + this.clazz + " tableName:" + this.tableName + " keyName:" + this.keyName);
}
public SessionImpl(DBHelper dbHelper) {
this.dbHelper = dbHelper;
}
@Override
public Serializable delete(Object entity) throws SQLException, ClassNotFoundException {
initProperites(entity.getClass());
Object keyValue = ClassUtil.getPropertie(entity, keyName);
String sql = "delete from {0} where {1}=?".replace("{0}", this.tableName).replace("{1}", keyName);
log.info("[delete]:" + sql);
log.info("[params]:" + keyValue);
return dbHelper.executeUpdate(sql, keyValue);
}
@Override
public Object getResource(Serializable id, Class clazz) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
initProperites(clazz);
String cols = buildStrColumn();
String sql = ClassUtil.formartString("SELECT {0} FROM {1} where {2}=?", new String[] { cols, this.tableName, this.keyName });
log.info("[get]:" + sql);
log.info("[params]:" + id);
RowSet rs = dbHelper.executeQuery(sql, id);
List list = ClassUtil.buildEntities(rs, clazz, allFields);
if (list.size() > 0)
return list.get(0);
return null;
}
// 插入
@Override
public Serializable insert(Object entity) throws Exception {
initProperites(entity.getClass());
String cols = buildStrColumn();// 要查詢的字段
String vs = buildValColumn();// 預編譯參數
String sql = ClassUtil.formartString("insert into {0}({1}) values({2})", new String[] { this.tableName, cols, vs });
Object[] params = ClassUtil.buildParams(entity, allFields);
return dbHelper.executeUpdate(sql, params);
}
@Override
public Serializable update(Object entity) throws SQLException, ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
initProperites(entity.getClass());
String cols = buildStrColumn();
String vs = buildValColumn();
String _cols = (cols + ",").replace(",", "=?,");
_cols = _cols.length() > 0 ? _cols.substring(0, _cols.length() - 1) : "*";
Object[] params = ClassUtil.buildParams(entity, allFields);
Object vKey = ClassUtil.buildParams(entity, keyName);
Object[] _params = new Object[params.length + 1];
for (int i = 0; i < params.length; i++) {
_params[i] = params[i];
}
_params[params.length] = vKey.toString();
String sql = ClassUtil.formartString("update {0} set {1} where {2}=? ", new String[] { this.tableName, _cols, this.keyName });
return dbHelper.executeUpdate(sql, _params);
}
@Override
public List query(Class clazz) throws SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
initProperites(clazz);
String cols = buildStrColumn();
String sql = ClassUtil.formartString("SELECT {0} FROM {1}", new String[] { cols, this.tableName });
RowSet rs = dbHelper.executeQuery(sql);
List list = ClassUtil.buildEntities(rs, clazz, allFields);
return list;
}
@Override
public int executeUpdate(String sql, Object... params) throws SQLException, ClassNotFoundException {
return dbHelper.executeUpdate(sql, params);
}
@Override
public RowSet executeQuery(String sql, Object... params) throws SQLException, ClassNotFoundException {
return dbHelper.executeQuery(sql, params);
}
@Override
public Object executeQueryUnique(String sql, Object... params) throws SQLException, ClassNotFoundException {
RowSet rs = dbHelper.executeQuery(sql, params);
if (rs.next())
return rs.getObject(1);
return null;
}
}
=====================================
/**
* 輔助類
* @author ypf
*/
public class ClassUtil {
/**
* 合併Field數組並去重,並實現過濾掉非Column字段,和實現Id放在首字段位置功能
* @param clazz
* @return 因此字段
*/
public static List<Field> joinFields(Class clazz) {
List<Class> cList = new ArrayList();
List<Field> fList = new ArrayList();
// 獲取全部類
findAllClass(clazz, cList);
// 獲取全部字段
for (Class c : cList) {
for (int i = 0; i < c.getDeclaredFields().length; i++) {
fList.add(c.getDeclaredFields()[i]);
}
}
Map<String, Field> map = new LinkedHashMap<String, Field>();
for (Field field : fList) {
// 過濾掉非Column定義的字段
if (!field.isAnnotationPresent(Column.class)) {
continue;
}
Column column = (Column) field.getAnnotation(Column.class);
map.put(column.name(), field);
}
List<Field> list = new ArrayList<Field>();
for (String key : map.keySet()) {
Field step = map.get(key);
// 若是是Id則放在首位置.
if (step.isAnnotationPresent(Id.class)) {
list.add(0, step);
} else {
list.add(step);
}
}
return list;
}
/**
* 查找全部類:自身及全部的父類(接口除外)
*
* @param clazz
* @param list
*/
private static void findAllClass(Class<?> clazz, List list) {
// 添加自身Class
if (list.size() == 0)
list.add(clazz);
Class<?> c = clazz.getSuperclass();
if (c != null) {
list.add(c);
findAllClass(c, list);
}
}
/**
* 建立類及封裝屬性
* @param clazz
* @param fileds
* @param values
* @return size
*/
public static Object buildProperties(Class clazz, List<Field> fileds, List values) throws InstantiationException, IllegalAccessException {
if (values.size() != fileds.size())
throw new RuntimeException("fileds的size和values的size不一致!");
Object obj = clazz.newInstance();
for (int i = 0; i < fileds.size(); i++) {
buildPropertie(obj, fileds.get(i), values.get(i));
}
return obj;
}
/**
* 給對象的某個字段封裝數值
*/
public static void buildPropertie(Object obj, Field f, Object value) {
try {
String methodName = buildSetMethod(f.getName());
// 給某個字段封裝數值,methodName方法名,obj.getClass().getMethod(buildGetMethod(f.getName())).getReturnType()返回類型
// invoke執行方法,obj要賦值的對象,value要賦的值
obj.getClass().getMethod(methodName, obj.getClass().getMethod(buildGetMethod(f.getName())).getReturnType()).invoke(obj, value);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 構建get方法
* @param fieldName
* @return String
*/
public static String buildGetMethod(String fieldName) {
String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return methodName;
}
/**
* 構建set方法
* @param fieldName
* @return String
*/
public static String buildSetMethod(String fieldName) {
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return methodName;
}
/**
* 根據字段名或對象對應的get方法值
* @param obj 對象
* @param fieldName字段名
* @return Object
*/
public static Object getPropertie(Object obj, String fieldName) {
Object val = "";
try {
String methodName = buildGetMethod(fieldName);
val = obj.getClass().getMethod(methodName).invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
return val;
}
/**
* 根據字段獲取對應的值
* @param entity
* @param allFields
* @return Object[]
*/
public static Object[] buildParams(Object entity, List<Field> fields) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object[] params = new Object[fields.size()];// 字段參數值
for (int i = 0; i < fields.size(); i++) {
Field f = fields.get(i);
String methodName = ClassUtil.buildGetMethod(f.getName());
Object v = entity.getClass().getMethod(methodName).invoke(entity);
if (v instanceof Enum) {
v = ((Enum) v).ordinal();
}
params[i] = v;
}
return params;
}
public static Object buildParams(Object entity, Field field) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
String methodName = ClassUtil.buildGetMethod(field.getName());
Object v = entity.getClass().getMethod(methodName).invoke(entity);
return v;
}
public static Object buildParams(Object entity, String fieldName) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
String methodName = ClassUtil.buildGetMethod(fieldName);
Object v = entity.getClass().getMethod(methodName).invoke(entity);
return v;
}
/**
* 格式化
* @param str
* @param params
* @return String
*/
public static String formartString(String str, Object... params) {
for (int i = 0; i < params.length; i++) {
Object obj = params[i];
if (obj != null)
str = str.replace("{" + i + "}", obj.toString());
}
return str;
}
public static List buildEntities(RowSet rs, Class clazz, List<Field> allFields) throws SQLException, InstantiationException, IllegalAccessException {
List<String> listCols = getRowSetColName(rs);
List list = new ArrayList();
while (rs.next()) {
List values = new ArrayList();
for (String key : listCols) {
values.add(rs.getObject(key));
}
Object obj = ClassUtil.buildProperties(clazz, allFields, values);
list.add(obj);
}
return list;
}
public static List getRowSetColName(RowSet rs) throws SQLException {
List collist = new ArrayList();
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
collist.add(rs.getMetaData().getColumnName(i + 1).toUpperCase());
}
return collist;
}
}