關於高淇JAVA中SORM總結學習筆記詳細我的解釋

                                                                                  代碼來源於高淇JAVA教學視頻 謝謝高淇老師的教學。html

由於本身在學習的過程當中發現了不少困難點,總結下但願對本身接下來學框架提高。給像我同樣得初學者方便。java

SORM框架是一個簡單的ORM,關係對象映射,能夠經過這個框架方便的更改和操做一些數據庫的東西,在框架運行的時候也會根據數據庫中的表生成相應的Javabean在po包下。經過直接對po包下的操做在運用query中的一些操做就能夠實現對數據庫的操做。思路會在代碼中註釋。mysql

QueryFactory直接生產出Query給用的人調用,Query做爲一個接口是更好的讓多種數據庫提供各自的操做。實現mySQLQuery或者OracleQuery,conventor是個轉換不一樣語言數據類型的工具sql

TableContext就是核心的程序鏈接數據庫表的類。DBManager鍾放的是一些配置資源文件和一些加載處理。數據庫

明確主要思路,就是經過factory製做一個query,而後直接set方法而後將對象傳入query的方法實現功能。查詢的話也是經過queryrows方法傳入select語句進行查詢,由於查詢語句的多樣性簡單的框架沒有進一步封裝。設計模式

 

 

先從最底層的tablecontext和DBManage提及:app

解釋會在代碼中標識框架

public class DBManager { private static Configuration conf;               //這是在bean包下定義的一個類用來把從配置文件提取出來方便用get set private static List<Connection> pool=new ArrayList<>();      //這是鏈接池的定義,我在老師的基礎上作了些更改,我把鏈接池所有放到了這個類裏 static{                                      //靜態代碼塊初始化資源文件中的數據, 注意靜態塊是和類一塊兒加載的,大量用靜態影響內存 Properties pro=new Properties();                      //這個類是用來從資源文件中提取數據的類 try { pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("database.properties")); } catch (IOException e) { // TODO Auto-generated catch block 
 e.printStackTrace(); } conf=new Configuration(); conf.setDriver(pro.getProperty("driver")); conf.setPoPackage(pro.getProperty("poPackage")); conf.setPwd(pro.getProperty("pwd")); conf.setScrPath(pro.getProperty("srcPath")); conf.setUser(pro.getProperty("user")); conf.setUrl(pro.getProperty("url")); conf.setUsingDB(pro.getProperty("usingDB")); conf.setQueryClass(pro.getProperty("queryClass")); conf.setMAX_POOL(Integer.parseInt(pro.getProperty("max_pool"))); conf.setMIN_POOL(Integer.parseInt(pro.getProperty("min_pool"))); System.out.println(TableContext.class);//加載類                          //這是用來加載表信息的,用於錶鏈接也能夠用反射來加載,可是要trycatch選擇輸出加載
 initPool();                                              //加載鏈接池 } public static void initPool(){                          //鏈接池的初始化 if(pool==null){ pool=new ArrayList<Connection>(); } while(pool.size()<conf.getMIN_POOL()){ pool.add(DBManager.createConn()); //初始化
 } } public static Connection getConn(){ //鏈接池取鏈接 int last_index=pool.size()-1; Connection conn=pool.get(last_index); pool.remove(last_index); return conn; } public static void CloseConn(Connection conn){                          //鏈接池關閉 if(pool.size()>conf.getMAX_POOL()){ try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block
 e.printStackTrace(); } }else { pool.add(conn); } } public static Connection createConn(){ try {                                                //真正的創建一個鏈接 Class.forName(conf.getDriver()); return DriverManager.getConnection(conf.getUrl(),conf.getUser(),conf.getPwd()); //直接創建鏈接
        } catch (Exception e) { // TODO: handle exception
 e.printStackTrace(); return null; } }                                                      //get用於配置信息的哪一個類,很好理解 public static Configuration getConf(){ return conf; }                                                     //以後的關閉就不說了,主要是要記得把關閉改變成從線程池中假關閉 public static void close(ResultSet rs,Statement ps,Connection conn){ try { if(rs!=null){ rs.close(); } } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); } try { if(ps!=null){ ps.close(); } } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); } try { if(conn!=null){ CloseConn(conn); } } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); } } public static void close(Statement ps,Connection conn){ try { if(ps!=null){ ps.close(); } } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); } try { if(conn!=null){ CloseConn(conn); } } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); } } }

而後如今有配置文件和鏈接了ide

接下來是錶鏈接tablecontect工具

/** * * 管理數據庫全部表結構和類結構的關係,並能夠根據表結構生成類結構 * @author NodeRed * */
public class TableContext { //最核心的 能夠根據表生成類。 /** * 表名爲key,信息對對象爲value */

    public static  Map<String,TableInfo> tables=new HashMap<>(); //這個表中的tableInfo也是定義的一個類大概意思就是可以記錄下整張表,這裏這個map就能記錄整個database
/** * 將po的class對象和表關聯 */ public static Map<Class, TableInfo> poClassTableMap=new HashMap<>(); //這個表用來記錄這個表後來會產生哪一個類,這後面會說 private TableContext(){}; //無參構造很重要 static{ try { Connection con=DBManager.getConn(); //這裏就得到了和數據庫的鏈接了 DatabaseMetaData dbmd=con.getMetaData(); //這裏原視頻中沒有說,大概是 能夠從鏈接中搜索獲取一些元數據,%是相似全部 ResultSet tableRet=dbmd.getTables(null, "%", "%",new String[]{"TABLE"}); //TABLE那就是要查詢的就是表了,可是這裏出現了問題,意外的搜索出了建立表的時間 System.out.println(tableRet); //記錄的那個表,捯飭了半天也沒解決。 而後以後是放到了一個結果集裏 while(tableRet.next()){ //遍歷查找出來的結果 String tableName=(String)tableRet.getObject("TABLE_NAME"); //得到表名 TableInfo ti=new TableInfo(tableName, new ArrayList<ColumnInFo>(), // 而後把表名先把上說的能夠記錄下整個表的new出來,具體的後面再說 new HashMap<String,ColumnInFo>()); tables.put(tableName, ti); //放到記錄整個數據庫的那個表裏 ResultSet set=dbmd.getColumns(null, "%", tableName ,"%"); //這裏根據表名獲取字段集, while(set.next()){ ColumnInFo ci=new ColumnInFo(set.getString("COLUMN_NAME"),set.getString("TYPE_NAME"),0);//能夠看出這裏獲取了字段的名字和類型 ti.getColumns().put(set.getString("COLUMN_NAME"),ci); //這裏是放到表映射,加載表的字段 } ResultSet set2=dbmd.getPrimaryKeys(null, "%", tableName); while(set2.next()){   //這裏加載主鍵 ColumnInFo ci2=(ColumnInFo)ti.getColumns().get(set2.getObject("COLUMN_NAME")); ci2.setKeyType(1); ti.getPriKeys().add(ci2); } if(ti.getPriKeys().size()>0){ ti.setOnlyPrivate(ti.getPriKeys().get(0)); } }                                  //若是這裏沒有懂得話,不要急,後面結合javabean //這裏就能夠理解爲,你在java中創建了一個表格,不是徹底填好數據得表格,只是單純得有每一個數據類型在哪一個表是否是主鍵得表示
有了這個標識之後能夠在query中找出數據庫要操做得是哪一個,再從query中操做數據庫。 }
catch (Exception e) { // TODO: handle exception } updataJavaPOFile();                        //由於咱們要再數據庫中操做,程序事先是不知道你表中得每一項叫什麼名字就沒辦法根據數據可定義好類,在用類來操做數據庫                         loadPOTables(); //這裏咱們就能夠根據數據庫中獲取的表框架,獲取表的名字,表中字段類型,生成這個數據庫的java類
//而後是每次加載這個類的時候更新 }   //一下是實現方法
public static void updataJavaPOFile(){ Map<String,TableInfo> map= TableContext.tables; 這裏就經過這個tables表而後用後面的一個java類實現了在項目中構造java類 // TableInfo t=map.get("new_table"); for(TableInfo t:map.values()){ JavaFileUtils.createJavaPOFile(t, new MysqlTypeConventor()); } } public static void loadPOTables(){ // Class c=Class.forName("com.cy.sorm.New_table"); // poClassTableMap.put(c, TableInfo); //這裏就是用反射把這個類和產生自哪一個表放在了一個表裏,在後面的操做有用 for(TableInfo tableInfo:tables.values()){ try{ Class c=Class.forName(DBManager.getConf().getPoPackage()+ "."+StringUTils.firstChar2UpCase(tableInfo.getTname())); poClassTableMap.put(c, tableInfo); }catch(Exception e){ e.printStackTrace(); } } } // public static void main(String[] args) { // Map<String, TableInfo> tables=TableContext.tables; // System.out.println(tables); // } }

 

 這裏也是orm的核心了,從數據庫中複製了一個映射表過來。可是老師說在工做中用的不多。接下來講一下字段映射和表映射吧,能夠從以上的javabean包中能夠看出,除了很容易理解的配置configuration,javasetget也就是在上面說的拼接咱們本身產生的java類的簡單結構。

private String name; 
    private String Datatype; 
    private int keyType;

字段就是在mysql建表時候的每一列

private String tname; /** * key 字段名 value字段類 */
    private Map<String,ColumnInFo> columns; private ColumnInFo onlyPrivate; private List<ColumnInFo> priKeys; 

這個表呢也就能夠記錄下整個表了 map中也就是整個表,記錄下主鍵 很好理解這樣咱們只要有足夠多的數據傳入咱們就能夠在java中改了整張表,可是咱們必去得作一個query類,用來我把這個數據類傳入就能夠在數據庫中同步更改。

而後大概思路也就成型了,用戶能夠提供數據庫 ,我自動給他生成他表中得類,他調用query中方法,對數據庫進行更改。可是怎麼經過我生成類對這個映射相聯繫,而後再映射到數據庫呢,咱們藏了一個map。

先說一點簡單得把tables中得到得數據進行生成java類

這裏實現了一個接口,能夠處理不一樣數據庫到java得翻譯數據類型這裏很好懂

public class MysqlTypeConventor implements TypeConvertor{ @Override public String databaseType2JavaType(String columnType) { if("varchar".equalsIgnoreCase(columnType)||"char".equalsIgnoreCase(columnType)){ return "String"; }else if("int".equalsIgnoreCase(columnType) ||"tinyint".equalsIgnoreCase(columnType)){ return "Integer"; }else if("double".equalsIgnoreCase(columnType)){ return "double"; }else if("timestamp".equalsIgnoreCase(columnType)){ return "Timestamp"; } return null; } @Override public String javaType2databaseType(String javaDataType) { // TODO Auto-generated method stub
        return null; } }

而後咱們把轉換器和傳進去的字段封裝成一個java生成類的工具,就是上面代碼提到的哪一個工具類

  

public class JavaFileUtils { public static JavaFieldGetSet createFieldGetSetSRC(ColumnInFo column,TypeConvertor convertor){ JavaFieldGetSet jfgs= new JavaFieldGetSet(); String javaFieldType = convertor.databaseType2JavaType(column.getDatatype()); jfgs.setFieldInfo("\tprivate "+javaFieldType+" "+column.getName()); StringBuilder getSrc=new StringBuilder(); getSrc.append("\tpublic "+javaFieldType+" get"+StringUTils.firstChar2UpCase(column.getName())+"(){\n"); getSrc.append("\t\treturn "+column.getName()+";\n"); getSrc.append("\t}\n"); jfgs.setGetInfo(getSrc.toString()); StringBuilder setSrc=new StringBuilder(); setSrc.append("\tpublic void set"+StringUTils.firstChar2UpCase(column.getName())+"("); setSrc.append(javaFieldType+" "+column.getName()+"){\n"); setSrc.append("\t\tthis."+column.getName()+"="+column.getName()+";\n"); setSrc.append("\t}\n"); jfgs.setSetInfo(setSrc.toString()); return jfgs; } public static String createJavaSrc(TableInfo tableInfo,TypeConvertor convertor){ Map<String,ColumnInFo> columns=tableInfo.getColumns();//表裝入map
        List<JavaFieldGetSet> javaFields=new ArrayList<>(); for(ColumnInFo c:columns.values()){ javaFields.add(createFieldGetSetSRC(c, convertor)); } StringBuilder src=new StringBuilder(); src.append("package "+DBManager.getConf().getPoPackage()+";\n\n"); src.append("import java.util.*;\n\n"); src.append("import java.sql.*;\n\n"); src.append("public class "+StringUTils.firstChar2UpCase(tableInfo.getTname())+"{\n\n"); for(JavaFieldGetSet f:javaFields){ src.append(f.getFieldInfo()+";\n"); } src.append("\n\n"); for(JavaFieldGetSet f:javaFields){ src.append(f.getSetInfo()); } src.append("\n"); for(JavaFieldGetSet f:javaFields){ src.append(f.getGetInfo()); } src.append("}\n"); // System.out.println(src);
        return src.toString(); } public static void createJavaPOFile(TableInfo tableInfo,TypeConvertor convertor){ String src=createJavaSrc(tableInfo, convertor); BufferedWriter bw=null; String srcPath=DBManager.getConf().getScrPath()+"\\"; String packagePath=DBManager.getConf().getPoPackage().replaceAll("\\.", "\\\\"); File f=new File(srcPath+packagePath.trim()); System.out.println(f); if(!f.exists()){ f.mkdirs(); } try { bw=new BufferedWriter(new FileWriter(f.getAbsoluteFile()+"\\"+StringUTils.firstChar2UpCase(tableInfo.getTname())+".java")); bw.write(src); } catch (Exception e) { // TODO: handle exception
        }finally { if(bw!=null){ try { bw.close(); } catch (IOException e) { // TODO Auto-generated catch block
 e.printStackTrace(); } } } } public static void main(String[] args) { // ColumnInFo ci=new ColumnInFo("username","varchar",0); // JavaFieldGetSet f=createFieldGetSetSRC(ci, new MysqlTypeConventor()); // System.out.println(f);
        Map<String,TableInfo> map= TableContext.tables; // TableInfo t=map.get("new_table");
        for(TableInfo t:map.values()){ JavaFileUtils.createJavaPOFile(t, new MysqlTypeConventor()); } } }

第一個方法就是把從字段類中獲取的名字類型轉換成一個String,這裏沒有表名因此拼接了的是getset,便於拼接是用stringbuilder拼接,

第二個方法有了表映射之後咱們就能夠拼接表名,而後從表映射中獲取有那些字段,再拼接上字段的setget方法,最後呢就是經過配置文件中得到咱們項目路徑,而後創建java類

這裏注意把「 .  」換成"  \\ "的操做java中一個變兩個

 

 

而後就是核心的實現query了,這裏原本是想實現接口 而後方便不一樣的數據庫鏈接不一樣的,而後後來由於方法重用的不少就改爲了抽象類。

public abstract class Query { public List executeQueryTemplate(String sql, Object[] params,Class clazz,CallBack back){ Connection conn=DBManager.getConn(); PreparedStatement ps=null; ResultSet rs=null; try { ps=conn.prepareStatement(sql); JDBCUtils.handleParams(ps, params); //這個是一個回調模板,經過callback,callback是一個簡單的接口,很經常使用,實現的做用就是,我實現一個接口,接口中是我要實現的功能 rs=ps.executeQuery(); //而後我能夠接着寫下去,這成爲了一個模板,只是中間這個方法的實現不同,當我要使用這個模板的時候調用這個方法,而後匿名內部類實現back return back.doExecute(conn, ps, rs); //中的方法可是匿名內部類又涉及到用外部變量 final的問題,我用過的也不多但願在以後的學習有更深的理解。 } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); return null; }finally { DBManager.close(rs,ps,conn); } } public int excuteDML(String sql,Object[] params){ Connection conn=DBManager.getConn(); int count=0; //這是個執行sql語句的封裝,object參數是傳入要操做的數由於用的是preparestatement PreparedStatement ps=null; try { ps=conn.prepareStatement(sql); JDBCUtils.handleParams(ps, params); count=ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block
 e.printStackTrace(); }finally{ DBManager.close(ps,conn); } return count; } public void insert(Object obj){ Class c=obj.getClass(); List<Object> params=new ArrayList<>();//儲存sql的參數對象 
        int countNotNull=0;//計算不爲空的屬性值
        TableInfo tableInfo = TableContext.poClassTableMap.get(c);//////////////////這就是map記錄了class和映射table的關係,而後返回映射表就能夠得到表名拼接sql語句了 StringBuilder sql=new StringBuilder("insert into "+tableInfo.getTname()+" ("); Field[] fs=c.getDeclaredFields(); //利用反射將對象的的名字和操做值而後拼接字符串 for(Field f:fs){ String fieldName=f.getName(); Object fieldValue=ReflectUtils.invokeGet(fieldName, obj); //這裏是利用反射 獲取get方法 get到咱們設置的值 而後拼接到sql語句中,
                                                  //這樣一來最開始的思路也解決了
if(fieldValue!=null){ countNotNull++; sql.append(fieldName+","); params.add(fieldValue); } } sql.setCharAt(sql.length()-1, ')'); sql.append(" value ("); for(int i=0;i<countNotNull;i++){ sql.append("?,"); } sql.setCharAt(sql.length()-1, ')'); excuteDML(sql.toString(), params.toArray()); } public void delete(Class clazz,Object id){ /** * table.class->delete from New_table where id=2; */ TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); ColumnInFo onlyPriKey=tableInfo.getOnlyPrivate(); //mysql刪除宇語句中咱們只要獲取表主鍵的編號就能夠刪除了 String sql="delete from "+tableInfo.getTname()+" where "+onlyPriKey.getName()+"=? "; excuteDML(sql, new Object[]{id}); } public void delete(Object obj){ Class c=obj.getClass(); TableInfo tableInfo=TableContext.poClassTableMap.get(c); //同理反射得到get方法 而後用第一個delete方法刪除 ColumnInFo onlyPriKey= tableInfo.getOnlyPrivate(); Object priKeyValue=ReflectUtils.invokeGet(onlyPriKey.getName(), obj); delete(c,priKeyValue); } public int update(Object obj,String[] fieldNames){ //obj("uname",pwd)--->update 表名 set uname=?,pwd=? where id =? Class c=obj.getClass(); List<Object> params=new ArrayList<>(); TableInfo tableInfo=TableContext.poClassTableMap.get(c); ColumnInFo priKey =tableInfo.getOnlyPrivate(); StringBuilder sql=new StringBuilder("update "+tableInfo.getTname()+" set "); for(String fname:fieldNames){ Object fvalue=ReflectUtils.invokeGet(fname, obj); params.add(fvalue); sql.append(fname+"=?,"); //和insert很像加了遍歷要更新的列 而後list記錄問好處要更新的值 反射獲取值 } sql.setCharAt(sql.length()-1, ' '); sql.append(" where "); sql.append(priKey.getName()+"=? "); params.add(ReflectUtils.invokeGet(priKey.getName(), obj)); return excuteDML(sql.toString(), params.toArray()); } //返回多行 public List queryRows(String sql, Class clazz, Object[] params){ return executeQueryTemplate(sql,params,clazz,new CallBack(){ ///接上回調方法的實現 public List doExecute(Connection conn, PreparedStatement ps, ResultSet rs) { List list=null; try { ResultSetMetaData metaData=rs.getMetaData(); while(rs.next()){ if(list==null){ list=new ArrayList(); } Object rowObj=clazz.newInstance();//調用javabeen的無參構造器 // select username,pwd,age from user where id>? and age<18 for(int i=0;i<metaData.getColumnCount();i++){ //getcolumnCount爲查詢要差的列如 username pwd age String columnName=metaData.getColumnLabel(i+1); Object columnValue=rs.getObject(i+1); ReflectUtils.invokeSet(rowObj, columnName, columnValue); } list.add(rowObj); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } return list; } }); } //返回一行 public Object queryUniqueRows(String sql, Class clazz,Object[] params){ List list=queryRows(sql, clazz, params); return (list==null&&list.size()>0)? null:list.get(0); } public Object queryValue(String sql,Object[] params){ Connection conn=DBManager.getConn(); Object value=null; PreparedStatement ps=null; ResultSet rs=null; try { ps=conn.prepareStatement(sql); JDBCUtils.handleParams(ps, params); rs=ps.executeQuery(); // rs中爲查到的行數 while(rs.next()){ value=rs.getObject(1); //rs結果集中不是以0開頭的 } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return value; } public Number queryNumber(String sql,Object[] params){ return (Number)queryValue(sql, params); } public abstract Object queryPagenate(int PageNum,int size); //抽象方法,不一樣子類不一樣實現 }

以後就能夠繼承query實現不一樣的數據庫的不一樣方法而後重寫和修改實現多態

 

而後實現了query後就能夠工廠設計模式,用factory單例,而後生產query  

public class QueryFactory { private static QueryFactory factory=new QueryFactory(); private static Class c; static{ try { c=Class.forName(DBManager.getConf().getQueryClass()); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block
 e.printStackTrace(); } } private QueryFactory() { // TODO Auto-generated constructor stub
 } public Query creatFactory(){ try { return (Query) c.newInstance(); } catch (InstantiationException e) { // TODO Auto-generated catch block
 e.printStackTrace(); return null; } catch (IllegalAccessException e) { // TODO Auto-generated catch block
 e.printStackTrace(); return null; } } }

 

原文出處:https://www.cnblogs.com/doyourwant/p/11792237.html

相關文章
相關標籤/搜索