关于高淇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

相关文章
相关标签/搜索