ORM框架:

• 我们希望设计一个可以实现对象和SQL自动映射的框架,但是整体用法和设计比Hibernate简单。砍掉不必要的功能。
• 会穿插使用设计模式
• 增加

– 将对象对应成sql语句,执行sql,插入数据库中

• 删除

– 根据对象主键的值,生成sql,执行,从库中删除

• 修改

– 根据对象需要修改属性的值,生成sql,执行• 查询
– 根据结果分类:

• 多行多列:List<Javabean>
• 一行多列:Javabean
• 一行一列:

– 普通对象:Object
– 数字:Number

基本思路:

获取db.properties配置的相关配置信息

将数据库的表转为对应的java实体类

封装表的信息及对应的java类型

利用反射获取类的相关信息/调用get/set方法

Class实例化对象

创建连接池

封装查询语句

核心架构:

Query接口:

负责查询(对外提供服务的核心类
package com.mikey.core;

import com.mikey.bean.ColumnInfo;
import com.mikey.bean.TableInfo;
import com.mikey.util.JDBCUtil;
import com.mikey.util.RefUtil; import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:30
* @Describe:
**/
public abstract class Query implements Cloneable{
/**
* 采用模板方法模式将JDBC操作封装成模板,便于重用
* @param sql
* @param params
* @param clazz
* @param callBack
* @return
*/
public Object executeQueryTemplate(String sql, Object[] params, Class clazz, CallBack callBack){ Connection connection=DBManager.getConnection(); PreparedStatement preparedStatement=null; ResultSet resultSet=null; try {
preparedStatement=connection.prepareStatement(sql);
JDBCUtil.handleParams(preparedStatement,params);
System.out.println(preparedStatement);
resultSet=preparedStatement.executeQuery();
return callBack.doExecute(connection,preparedStatement,resultSet);
}catch (Exception e){
e.printStackTrace();
return null;
}finally {
DBManager.close(preparedStatement,connection);
}
} /**
* 执行一个DML语句
* @param sql
* @param params
* @return 执行sql语句后影响的行数
*/
public int executeDML(String sql,Object[] params){
Connection connection=DBManager.getConnection();
int count=;
PreparedStatement preparedStatement=null;
try {
preparedStatement=connection.prepareStatement(sql);
JDBCUtil.handleParams(preparedStatement,params);
System.out.println(preparedStatement);
count=preparedStatement.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
DBManager.close(preparedStatement,connection);
}
return count;
} /**
* 将一个对象存储到数据库中
* @param obj
*/
public void insert(Object obj){
/**
* 获取传入对象的Class
*/
Class clazz=obj.getClass();
/**
* 存储sql的参数对象
*/
ArrayList<Object> params = new ArrayList<>();
/**
* 表信息
*/
TableInfo tableInfo=TableContext.poClassTableMap.get(clazz);
/**
* 构建sql
*/
StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
/**
* 计算不为null的属性值
*/
int countNotNullField=;
/**
* 通过反射获取所有属性
*/
Field[] fileds=clazz.getDeclaredFields();
/**
* 遍历构建SQL
*/
for (Field field:fileds) {
String fieldName=field.getName();
Object fieldValue= RefUtil.invokeGet(fieldName,obj); if(fieldValue!=null){
countNotNullField++;
sql.append(fieldName+",");
params.add(fieldValue);
}
}
sql.setCharAt(sql.length()-,')');
sql.append(" values (");
for (int i = ; i < countNotNullField; i++) {
sql.append("?,");
}
sql.setCharAt(sql.length()-,')');
executeDML(sql.toString(),params.toArray());
} /**
* 删除clazz表示类对应的表中的记录(指定主键值id的记录)
* @param clazz
* @param id
*/
public void delete(Class clazz,Object id){
//Emp.class,2-->delete from emp where id=2
//通过Class对象找TableInfo
TableInfo tableInfo=TableContext.poClassTableMap.get(clazz);
//获取主键
ColumnInfo onlyPriKey=tableInfo.getOnlyPrikey();
//sql
String sql = "delete from "+tableInfo.getTname()+ " where "+onlyPriKey.getName()+"=?";
//execute
executeDML(sql,new Object[]{id});
} /**
* 删除对象在数据库中对应的记录(对象所在的类对应到表,对象的主键的值对应到记录)
* @param obj
*/
public void delete(Object obj){
Class clazz=obj.getClass(); TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); ColumnInfo onlyPrikey=tableInfo.getOnlyPrikey(); Object prikeyValue=RefUtil.invokeGet(onlyPrikey.getName(),obj); delete(clazz,prikeyValue);
} /**
* 更新对象对应的记录
* @param obj
* @param fieldNames
* @return
*/
public int update(Object obj,String[] fieldNames){
//obj{"uanme","pwd"}-->update 表名 set uname=?,pwd=? where id=? Class clazz=obj.getClass(); List<Object> params=new ArrayList<>(); TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); ColumnInfo onlyPrikey = tableInfo.getOnlyPrikey(); StringBuilder sql = new StringBuilder("update "+tableInfo.getTname()+ " set "); for (String fname: fieldNames) {
Object fvalue=RefUtil.invokeGet(fname,obj);
params.add(fvalue);
sql.append(fname+"=?,");
}
sql.setCharAt(sql.length()-,' ');
sql.append(" where ");
sql.append(onlyPrikey.getName()+"=? "); params.add(RefUtil.invokeGet(onlyPrikey.getName(),obj));
return executeDML(sql.toString(),params.toArray());
} /**
* 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中
* @param sql
* @param clazz
* @param params
* @return
*/
public List queryRows(String sql,Class clazz,Object[] params){
return (List)executeQueryTemplate(sql,params,clazz, new CallBack() {
@Override
public Object doExecute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) { List list=null; try {
ResultSetMetaData metaData= resultSet.getMetaData();
//多行
while (resultSet.next()){
if (list==null){
list=new ArrayList();
}
/**
* 调用无参构造方法
*/
Object rowObj=clazz.newInstance(); for (int i = ; i < metaData.getColumnCount(); i++) {
String columnName=metaData.getColumnLabel(i+);
Object columnValue=resultSet.getObject(i+); RefUtil.invokeSet(rowObj,columnName,columnValue);
}
list.add(rowObj);
} }catch (Exception e){
e.printStackTrace();
}
return list;
}
});
} /**
* 查询一行记录
* 查询返回一行记录,并将该记录封装到clazz指定的类的对象中
* @param sql
* @param clazz
* @param params
* @return
*/
public Object queryUniqueRow(String sql,Class clazz,Object[] params){ List list=queryRows(sql,clazz,params); return (list!=null&&list.size()>?list.get():null);
} /**
* 查询一个值
* 根据主键的值直接查找对应的对象
* @param sql
* @param params
* @return
*/
public Object queryVlaue(String sql,Object[] params){
return executeQueryTemplate(sql, params, null, new CallBack() {
@Override
public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
Object value = null;
try {
while(rs.next()){
value = rs.getObject();
}
} catch (SQLException e) {
e.printStackTrace();
}
return value;
}
});
} /**
* 查询一个数值
* 查询返回一个数字(一行一列),并将该值返回
* @param sql
* @param clazz
* @param params
* @return
*/
public Number queryNumber(String sql,Class clazz,Object[] params){
return (Number)queryVlaue(sql,params);
}
/**
* 分页查询
* @param pageNum 第几页数据
* @param size 每页显示多少记录
* @return
*/
public abstract Object queryPagenate(int pageNum,int size); @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

Query

QueryFactory类:

负责根据配置信息创建query对象
package com.mikey.core;

import com.sun.org.apache.regexp.internal.RE;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:42
* @Describe:
**/
public class QueryFactory { // public QueryFactory() {
// } //原型对象
private static Query prototypeObj; static { try { Class clazz=Class.forName(DBManager.getConf().getQueryClass()); prototypeObj=(Query)clazz.newInstance(); }catch (Exception e){ e.printStackTrace(); } TableContext.loadPOTables();
} /**
* 私有化构造器
*/
private QueryFactory(){ }
/**
* createQuery
* @return
*/
private static Query createQuery(){
try {
return (Query)prototypeObj.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}

QueryFactory

TypeConvertor接口:

负责类型转换
package com.mikey.core;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:43
* @Describe:
**/
public interface TypeConvertor { /**
* 将数据库类型转换为java类型
* @param columnType
* @return
*/
public String databaseType2JavaType(String columnType); /**
* 将java类型转换为数据库类型
* @param javaDataType
* @return
*/
public String javaType2DatabaseType(String javaDataType); }

TypeConvertor

TableContext类:

负责获取管理数据库所有表结构和类结构的关系,并可以根据表结构生成类结构。
package com.mikey.core;

import com.mikey.bean.ColumnInfo;
import com.mikey.bean.TableInfo;
import com.mikey.util.JavaFileUtil;
import com.mikey.util.StringUtil; import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:49
* @Describe:
* 负责获取管理数据库所有表结构和类结构的关系,
* 并可以根据表结构生成类结构。
**/
public class TableContext {
/**
* 表名Key 表信息对象为value
*/
public static Map<String, TableInfo> tables=new HashMap<String, TableInfo>();
/**
* 将po的class的对象和表信息对象关联起来便于重用!
*/
public static Map<Class,TableInfo> poClassTableMap=new HashMap<Class, TableInfo>(); private TableContext(){} static {
try {
Connection connection=DBManager.getConnection();
DatabaseMetaData metaData=connection.getMetaData(); ResultSet tableRet=metaData.getTables(null,"%","%",new String[]{"TABLE"}); while (tableRet.next()){
String tableName=(String)tableRet.getObject("TABLE_NAME");
TableInfo tableInfo=new TableInfo(tableName,new HashMap<String,ColumnInfo>(),new ArrayList<ColumnInfo>());
tables.put(tableName,tableInfo); ResultSet set=metaData.getColumns(null,"%",tableName,"%");
while(set.next()){
ColumnInfo ci = new ColumnInfo(set.getString("COLUMN_NAME"),
set.getString("TYPE_NAME"), 0);
tableInfo.getColumnInfoMap().put(set.getString("COLUMN_NAME"), ci);
} ResultSet set2=metaData.getPrimaryKeys(null,"%",tableName);
while (set2.next()){
ColumnInfo ci2=(ColumnInfo)tableInfo.getColumnInfoMap().get(set2.getObject("COLUMN_NAME"));
ci2.setKeyType(0);
tableInfo.getPriKeys().add(ci2);
} if (tableInfo.getPriKeys().size()>0){
tableInfo.setOnlyPrikey(tableInfo.getPriKeys().get(0));
}
} }catch (Exception e){
e.printStackTrace();
}
} /**
* 根据表结构,更新配置的po包下面的java类
* 实现了从表结构转化到类结构
*/
public static void updateJavaPOFile(){
Map<String,TableInfo> map=TableContext.tables;
for (TableInfo t:map.values()) {
JavaFileUtil.createJavaPOFile(t,new MySqlTypeConvertor());
}
} public static void loadPOTables() {
for (TableInfo tableInfo : tables.values()) {
try {
Class clazz = Class.forName(DBManager.getConf().getPoPackage()
+ "." + StringUtil.firstChar2UpperCase(tableInfo.getTname()));
poClassTableMap.put(clazz, tableInfo); } catch (Exception e) {
e.printStackTrace();
}
}
} public static void main(String[] args){ Map<String, TableInfo> tables = TableContext.tables; System.out.println(tables); }
}

TableContext

DBManager类:

根据配置信息,维持连接对象的管理(增加连接池功能) 
package com.mikey.core;

import com.mikey.bean.Configuration;
import com.mikey.pool.DBConnPool; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:50
* @Describe: 根据配置信息
**/
public class DBManager { /**
* 配置信息类
*/
private static Configuration conf; /**
* 连接对象
*/
private static DBConnPool pool; static {
Properties pros=new Properties();
try {
pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
}catch (Exception e){
e.printStackTrace();
} conf=new Configuration(); conf.setDriver(pros.getProperty("driver"));
conf.setPoPackage(pros.getProperty("poPackage"));
conf.setPwd(pros.getProperty("pwd"));
conf.setSrcPath(pros.getProperty("srcPath"));
conf.setUrl(pros.getProperty("url"));
conf.setUser(pros.getProperty("user"));
conf.setUsingDB(pros.getProperty("usingDB"));
conf.setQueryClass(pros.getProperty("queryClass"));
conf.setPoolMaxSize(Integer.parseInt(pros.getProperty("poolMaxSize")));
conf.setPoolMinSize(Integer.parseInt(pros.getProperty("poolMinSize")));
} /**
* 获取连接对象
* @return
*/
public static Connection getConnection(){
if (pool==null){
pool=new DBConnPool();
}
return pool.getConnection();
} /**
* 创建连接
* @return
*/
public static Connection createConnection(){
try {
Class.forName(conf.getDriver());
return DriverManager.getConnection(conf.getUrl(),conf.getUser(),conf.getPwd());
}catch (Exception e){
e.printStackTrace();
return null;
}
} /**
* 关闭传入的相关资源对象
* @param resultSet
* @param statement
* @param connection
*/
public static void close(ResultSet resultSet, Statement statement,Connection connection){
try {
if (resultSet!=null){
resultSet.close();
}
}catch (Exception e){
e.printStackTrace();
}
try {
if (statement!=null){
statement.close();
}
}catch (Exception e){
e.printStackTrace();
} pool.close(connection);
} /**
* 关闭Statement返回连接对象到连接池
* @param statement
* @param connection
*/
public static void close(Statement statement,Connection connection){
try {
if (statement!=null){
statement.close();
}
}catch (Exception e){
e.printStackTrace();
}
pool.close(connection);
} /**
* 返回连接对象到连接池
* @param connection
*/
public static void close(Connection connection){
pool.close(connection);
} /**
* 返回Configuration对象
* @return
*/
public static Configuration getConf(){
return conf;
} }

DBManager

接口回调:

package com.mikey.core;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 16:46
* @Describe:
**/
public interface CallBack {
public Object doExecute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet);
}

CallBack

package com.mikey.core;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 17:10
* @Describe:
**/
public class MySqlQuery extends Query {
}

MySqlQuery

package com.mikey.core;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 18:04
* @Describe:
**/
public class MySqlTypeConvertor implements TypeConvertor{
@Override
public String databaseType2JavaType(String columnType) { //varchar-->String
if("varchar".equalsIgnoreCase(columnType)||"char".equalsIgnoreCase(columnType)){
return "String";
}else if("int".equalsIgnoreCase(columnType)
||"tinyint".equalsIgnoreCase(columnType)
||"smallint".equalsIgnoreCase(columnType)
||"integer".equalsIgnoreCase(columnType)
){
return "Integer";
}else if("bigint".equalsIgnoreCase(columnType)){
return "Long";
}else if("double".equalsIgnoreCase(columnType)||"float".equalsIgnoreCase(columnType)){
return "Double";
}else if("clob".equalsIgnoreCase(columnType)){
return "java.sql.CLob";
}else if("blob".equalsIgnoreCase(columnType)){
return "java.sql.BLob";
}else if("date".equalsIgnoreCase(columnType)){
return "java.sql.Date";
}else if("time".equalsIgnoreCase(columnType)){
return "java.sql.Time";
}else if("timestamp".equalsIgnoreCase(columnType)){
return "java.sql.Timestamp";
} return null; } @Override
public String javaType2DatabaseType(String javaDataType) {
return null;
}
}

MySqlTypeConvertor

连接池:

package com.mikey.pool;

import com.mikey.core.DBManager;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 19:39
* @Describe:连接池
**/
public class DBConnPool {
/**
* 连接池对象
*/
private List<Connection> pool;
/**
* 最大连接数
*/
public static final int POOL_MAX_SIZE= DBManager.getConf().getPoolMaxSize();
/**
* 最小连接数
*/
public static final int POOL_MIN_SIZE=DBManager.getConf().getPoolMinSize();
/**
* 初始化连接池,使池中的连接数达到最小值
*/
public void initPool(){
if (pool==null){
pool=new ArrayList<Connection>();
}
while (pool.size() < DBConnPool.POOL_MIN_SIZE){
pool.add(DBManager.createConnection());
System.out.println("初始化数据库连接池:"+pool.size());
} }
/**
* 从连接池中取出一个连接
* @return
*/
public synchronized Connection getConnection(){
int last_index=pool.size()-1;
Connection connection=pool.get(last_index);
pool.remove(last_index);
return connection;
} /**
* 将连接放回池中
* @param connection
*/
public synchronized void close(Connection connection){
if (pool.size()>=POOL_MAX_SIZE){
try {
if (connection!=null){
connection.close();
}
}catch (Exception e){
e.printStackTrace();
}
}else {
pool.add(connection);
}
} /**
* 构造器初始化
*/
public DBConnPool(){
initPool();
} }

DBConnPool

工具类:

JDBCUtils封装常用JDBC操作 
package com.mikey.util;

import java.sql.PreparedStatement;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:50
* @Describe:封装了JDBC查询常用的操作
**/
public class JDBCUtil { public static void handleParams(PreparedStatement preparedStatement,Object[] params) {
if (params!=null){
for (int i = 0; i < params.length; i++) {
try {
preparedStatement.setObject(1+i,params[i]);
}catch (Exception e){
e.printStackTrace();
}
}
}
} }

JDBCUtil

JavaFileUtils封装java文件操作
package com.mikey.util;

import com.mikey.bean.ColumnInfo;
import com.mikey.bean.JavaFieldGetSet;
import com.mikey.bean.TableInfo;
import com.mikey.core.DBManager;
import com.mikey.core.MySqlTypeConvertor;
import com.mikey.core.TableContext;
import com.mikey.core.TypeConvertor; import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:52
* @Describe:封装了生成Java文件(源代码)常用的操作
**/
public class JavaFileUtil {
/**
* 根据字段信息生成java属性信息。如:varchar username-->private String username;以及相应的set和get方法源码
* @param columnInfo
* @param typeConvertor
* @return
*/
public static JavaFieldGetSet createFieldGetSetSRC(ColumnInfo columnInfo, TypeConvertor typeConvertor){ JavaFieldGetSet jfgs = new JavaFieldGetSet(); String javaFieldType = typeConvertor.databaseType2JavaType(columnInfo.getDataType()); jfgs.setFieldInfo("\tprivate "+javaFieldType +" "+columnInfo.getName()+";\n"); //public String getUsername(){return username;}
//生成get方法的源代码
StringBuilder stringBuilde=new StringBuilder();
stringBuilde.append("\tpublic "+javaFieldType+" get"+StringUtil.firstChar2UpperCase(columnInfo.getName())+"(){\n");
stringBuilde.append("\t\treturn "+columnInfo.getName()+";\n");
stringBuilde.append("\t}\n");
jfgs.setGetInfo(stringBuilde.toString()); //public void setUsername(String username){this.username=username;}
//生成set方法的源代码
StringBuilder setSrc = new StringBuilder();
setSrc.append("\tpublic void set"+StringUtil.firstChar2UpperCase(columnInfo.getName())+"(");
setSrc.append(javaFieldType+" "+columnInfo.getName()+"){\n");
setSrc.append("\t\tthis."+columnInfo.getName()+"="+columnInfo.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.getColumnInfoMap();
List<JavaFieldGetSet> javaFields = new ArrayList<JavaFieldGetSet>(); for (ColumnInfo columnInfo:columns.values()){
javaFields.add(createFieldGetSetSRC(columnInfo,convertor));
} StringBuilder stringBuilder = new StringBuilder(); //生成package
stringBuilder.append("package "+ DBManager.getConf().getPoPackage()+";\n\n");
//生成import
stringBuilder.append("import java.sql.*;\n");
stringBuilder.append("import java.util.*;\n");
//生成类声明语句
stringBuilder.append("public class "+StringUtil.firstChar2UpperCase(tableInfo.getTname())+" {\n\n"); //生成属性列表
for (JavaFieldGetSet javaFieldGetSet:javaFields){
stringBuilder.append(javaFieldGetSet.getFieldInfo());
}
stringBuilder.append("\n\n");
//生成get方法
for (JavaFieldGetSet javaFieldGetSet:javaFields){
stringBuilder.append(javaFieldGetSet.getGetInfo());
}
//生成set方法
for (JavaFieldGetSet javaFieldGetSet:javaFields){
stringBuilder.append(javaFieldGetSet.getSetInfo());
} stringBuilder.append("}\n");
return stringBuilder.toString(); } public static void createJavaPOFile(TableInfo tableInfo,TypeConvertor convertor){
String src = createJavaSrc(tableInfo,convertor); String srcPath = DBManager.getConf().getSrcPath()+"\\"; String packagePath = DBManager.getConf().getPoPackage().replaceAll("\\.","/"); File file=new File(srcPath+packagePath); if (!file.exists()){
file.mkdirs();
} BufferedWriter bufferedWriter = null; try {
bufferedWriter=new BufferedWriter(new FileWriter(file.getAbsoluteFile()+"/"+StringUtil.firstChar2UpperCase(tableInfo.getTname())+".java"));
bufferedWriter.write(src);
System.out.println("建立表:"+tableInfo.getTname()+"对应的java类:"+StringUtil.firstChar2UpperCase(tableInfo.getTname()+".java")); }catch (Exception e){
e.printStackTrace();
}finally {
try {
if (bufferedWriter!=null){
bufferedWriter.close();
}
}catch (Exception e){
e.printStackTrace();
}
} } public static void main(String[] args){ Map<String,TableInfo> map = TableContext.tables; for (TableInfo tableInfo:map.values()){
createJavaPOFile(tableInfo,new MySqlTypeConvertor());
} }
}

JavaFileUtil

StringUtils封装常用字符串操作
package com.mikey.util;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:51
* @Describe:封装了字符串常用的操作
**/
public class StringUtil {
/**
* 将目标字符串首字母变为大写
* @param str
* @return
*/
public static String firstChar2UpperCase(String str){
//abcd--->Abcd
//abcd--->ABCD--->Abcd
return str.toUpperCase().substring(0,1)+str.substring(1);
}
}

StringUtil

ReflectUtils封装常用反射操作
package com.mikey.util;

import java.lang.reflect.Method;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:51
* @Describe:反射工具类
**/
public class RefUtil {
/**
* 调用obj对象对应属性fieldName的get方法
* @param fieldName
* @param obj
* @return
*/
public static Object invokeGet(String fieldName,Object obj){
try {
Class clazz=obj.getClass();
Method method=clazz.getMethod("get"+StringUtil.firstChar2UpperCase(fieldName),null);
return method.invoke(obj,null);
}catch (Exception e){
e.printStackTrace();
return null;
}
} /**
* 调用obj对象对应属性fieldName的set方法
* @param obj
* @param columnName
* @param columnValue
*/
public static void invokeSet(Object obj,String columnName,Object columnValue) {
try {
if (columnValue!=null){
Method method=obj.getClass().getDeclaredMethod("set"+StringUtil.firstChar2UpperCase(columnName),null);
method.invoke(obj,columnValue);
}
}catch (Exception e){
e.printStackTrace();
}
}
}

RefUtil

核心bean,封装相关数据:

ColumnInfo:封装表中一个字段的信息(字段类型、字段名、键类型)

package com.mikey.bean;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:52
* @Describe:
* 封装一个字段的信息
**/
public class ColumnInfo { private String name;
//字段名称
private String dataType;
//数据类型
private int keyType;
//字段的健类型 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getDataType() {
return dataType;
} public void setDataType(String dataType) {
this.dataType = dataType;
} public int getKeyType() {
return keyType;
} public void setKeyType(int keyType) {
this.keyType = keyType;
} public ColumnInfo(String name, String dataType, int keyType) {
this.name = name;
this.dataType = dataType;
this.keyType = keyType;
}
}

ColumnInfo

Configuration:封装配置文件信息

package com.mikey.bean;

/**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:55
* @Describe:管理配置信息
**/
public class Configuration {
/**
* 驱动类
*/
private String driver; /**
* jdbc的url
*/
private String url; /**
* 数据库用户名
*/
private String user;
/**
* 数据库密码
*/
private String pwd;
/**
* 正在使用哪个数据库
*/
private String usingDB;
/**
* 项目的源码路径
*/
private String srcPath;
/**
* 扫描生成的java类的包(持久画对象)
*/
private String poPackage;
/**
* 项目使用的查询类的包
*/
private String queryClass;
/**
* 连接池中最小的连接数
*/
private int poolMinSize;
/**
* 连接池中最大连接数
*/
private int poolMaxSize; public Configuration() {
} public Configuration(String driver, String url, String user, String pwd, String usingDB, String srcPath, String poPackage, String queryClass, int poolMinSize, int poolMaxSize) {
this.driver = driver;
this.url = url;
this.user = user;
this.pwd = pwd;
this.usingDB = usingDB;
this.srcPath = srcPath;
this.poPackage = poPackage;
this.queryClass = queryClass;
this.poolMinSize = poolMinSize;
this.poolMaxSize = poolMaxSize;
} public String getDriver() {
return driver;
} public void setDriver(String driver) {
this.driver = driver;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public String getUser() {
return user;
} public void setUser(String user) {
this.user = user;
} public String getPwd() {
return pwd;
} public void setPwd(String pwd) {
this.pwd = pwd;
} public String getUsingDB() {
return usingDB;
} public void setUsingDB(String usingDB) {
this.usingDB = usingDB;
} public String getSrcPath() {
return srcPath;
} public void setSrcPath(String srcPath) {
this.srcPath = srcPath;
} public String getPoPackage() {
return poPackage;
} public void setPoPackage(String poPackage) {
this.poPackage = poPackage;
} public String getQueryClass() {
return queryClass;
} public void setQueryClass(String queryClass) {
this.queryClass = queryClass;
} public int getPoolMinSize() {
return poolMinSize;
} public void setPoolMinSize(int poolMinSize) {
this.poolMinSize = poolMinSize;
} public int getPoolMaxSize() {
return poolMaxSize;
} public void setPoolMaxSize(int poolMaxSize) {
this.poolMaxSize = poolMaxSize;
}
}

Configuration

TableInfo:封装一张表的信息

package com.mikey.bean;

import java.util.List;
import java.util.Map; /**
* @Program: ORM
* @Author: 麦奇
* @Email: 1625017540@qq.com
* @Create: 2019-04-06 15:56
* @Describe:表信息
**/
public class TableInfo {
//表名
private String tname;
//表的所有字段
private Map<String,ColumnInfo> columnInfoMap;
//唯一主健
private ColumnInfo onlyPrikey;
//联合主键
private List<ColumnInfo> priKeys; public String getTname() {
return tname;
} public void setTname(String tname) {
this.tname = tname;
} public Map<String, ColumnInfo> getColumnInfoMap() {
return columnInfoMap;
} public void setColumnInfoMap(Map<String, ColumnInfo> columnInfoMap) {
this.columnInfoMap = columnInfoMap;
} public ColumnInfo getOnlyPrikey() {
return onlyPrikey;
} public void setOnlyPrikey(ColumnInfo onlyPrikey) {
this.onlyPrikey = onlyPrikey;
} public List<ColumnInfo> getPriKeys() {
return priKeys;
} public void setPriKeys(List<ColumnInfo> priKeys) {
this.priKeys = priKeys;
} public TableInfo() {
} public TableInfo(String tname, Map<String, ColumnInfo> columnInfoMap, ColumnInfo onlyPrikey) {
this.tname = tname;
this.columnInfoMap = columnInfoMap;
this.onlyPrikey = onlyPrikey;
} public TableInfo(String tname, Map<String, ColumnInfo> columnInfoMap, List<ColumnInfo> priKeys) {
this.tname = tname;
this.columnInfoMap = columnInfoMap;
this.priKeys = priKeys;
}
}

TableInfo

driver=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/sorm
user=root
pwd=
usingDB=mysql
srcPath=D\:\\workspace\\SORM\\src
poPackage=com.mikey.po
queryClass=com.mikey.sorm.core.MySqlQuery
poolMinSize=
poolMaxSize=

db.properties

架构图:

• 针对SORM框架的说明:
核心思想:使用简单、性能高、极易上手!
– 配置文件
• 目前使用资源文件、后期项目复杂后可以增加XML文件配置和注解。
– 类名由表名生成,只有首字母大写有区别,其他无区别
– Java对象的属性由表中字段生成,完全对应
– 目前,只支持表中只有一个主键,联合主键不支持

回调接口

public interface CallBack {
public Object doExecute(Connection conn,PreparedStatement ps,ResultSet rs);
}

模板方法

public Object executeQueryTemplate(String sql,Object[] params,Class clazz,CallBack back){
Connection conn = DBManager.getConn();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
//给sql设参
JDBCUtils.handleParams(ps, params);
System.out.println(ps);
rs = ps.executeQuery();
return back.doExecute(conn, ps, rs);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}finally{
DBManager.close(ps, conn);
}• 调用示例
public Object queryValue(String sql,Object[] params){
return executeQueryTemplate(sql, params, null, new CallBack() {
@Override
public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
Object value = null;
try {
while(rs.next()){
value = rs.getObject();
}
} catch (SQLException e) {
e.printStackTrace();
}
return value;
}
});
}

executeQueryTemplate

• 使用工厂模式统计管理Query的创建
• 使用克隆模式提高Query对象的创建效率

• 连接池(Connection Pool)
– 就是将Connection对象放入List中,反复重用!
– 连接池的初始化:
• 事先放入多个连接对象。
– 从连接池中取连接对象
• 如果池中有可用连接,则将池中最后一个返回。
同时,将该连接从池中remove,表示正在使用。
• 如果池中无可用连接,则创建一个新的。
– 关闭连接
• 不是真正关闭连接,而是将用完的连接放入池中。

• 市面上的连接池产品:

– DBCP
– c3p0
– proxool 

实现代码:

代码结构:

代码地址

实现简单ORM案例的更多相关文章

  1. 简单登录案例(SharedPreferences存储账户信息)&联网请求图片并下载到SD卡(文件外部存储)

    新人刚学习Android两周,写一个随笔算是对两周学习成果的巩固,不足之处欢迎各位建议和完善. 这次写的是一个简单登录案例,大概功能如下: 注册的账户信息用SharedPreferences存储: 登 ...

  2. MyBatis学习总结(一)简单入门案例

    MyBatis学习总结(一)简单入门案例 主要内容:本文主要通过对数据库中的use表进行增删改查总结mybatis的环境搭建和基本入门使用 一.需要的jar包: 1.核心包 2.依赖包 3.jdbc数 ...

  3. 一个简单的案例带你入门Dubbo分布式框架

    相信有很多小伙伴都知道,dubbo是一个分布式.高性能.透明化的RPC服务框架,提供服务自动注册.自动发现等高效服务治理方案,dubbo的中文文档也是非常全的,中文文档可以参考这里dubbo.io.由 ...

  4. solr简单搜索案例

    solr简单搜索案例 使用Solr实现电商网站中商品信息搜索功能,可以根据关键字搜索商品信息,根据商品分类.价格过滤搜索结果,也可以根据价格进行排序,实现分页. 架构分为: 1. solr服务器 2. ...

  5. springcloud+eureka简单入门案例

    springcloud+eureka简单入门案例 一.服务提供者 直接提供服务,入门案例没有特别要设置的地方,注意下端口,由于要启动多个服务,可能会冲突 配置文件(src/main/resources ...

  6. Python 简单爬虫案例

    Python 简单爬虫案例 import requests url = "https://www.sogou.com/web" # 封装参数 wd = input('enter a ...

  7. arduino中SCoop库的简单应用案例

    转载:https://www.csdn.net/gather_27/MtTaggzsMDExMS1ibG9n.html arduino中SCoop库的简单应用案例首先这篇文章来在视频https://v ...

  8. ReentrantReadWriteLock读写锁简单原理案例证明

    ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...

  9. redux 的简单实用案例

    redux 的简单实用案例 整体思想与结构 创建一个Action 创建一个Reducer 创建Store 在App组件开始使用 整体思想与结构 文件目录如下: 构建 action,通过创建一个函数,然 ...

随机推荐

  1. 2.10 webdriver中 js 使用

    来源: 使用Webdriver执行JS小结  http://lijingshou.iteye.com/blog/2018929 selenium常用的js总结  http://www.cnblogs. ...

  2. 2.7.1 元素定位:selenium消息框处理 (alert、confirm、prompt)

    来源:http://blog.csdn.net/cui_angel/article/details/7784211        http://www.cnblogs.com/tobecrazy/p/ ...

  3. SpringMVC项目使用elastic search搜索

    项目需要,引入了elastic search(后续简称es),后面将介绍本地对es的安装,使用以及java连接es查询的整个过程. 1.es索引字段建立与修改,以curl新增一个索引字段示例 curl ...

  4. Linux - cron - 基础

    概述 cron 相关的理解与使用 背景 最近实在没啥写的了 我写东西, 一般是是这些 看了书过后, 做一些系统的整理 比如之前的 docker 和 git 系列 遇到了实际问题, 解决过程也不是那么顺 ...

  5. AWS ec2的ubuntu14.04上安装git服务

    http://imerc.xyz/2015/11/13/Ubuntu-14-04%E4%B8%8AGit%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E6%90%AD%E5 ...

  6. 关于微信小程序的分包

    最近开始一份新的工作,接手一个正在开发的小程序,第一步添加客服系统,我们用的网易七鱼,利用微信小程序SDK接入的方式,一顿操作之后,欧欧~~!!~~~,提示没法预览, 究其原因,资源包过大,微信小程序 ...

  7. 一起了解 .Net Foundation 项目 No.3

    .Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. AutoMapper Au ...

  8. django入门与实践(开)

    1.什么是Django? 基于python的高级web开发框架 高效 快速 免费 开源 正常上网流程 浏览器浏览网页的基本原理 请求响应过程 开发环境搭建 Python Django pip inst ...

  9. 如何在Access中使用SQL语句

    如何在Access中使用SQL语句 创建,查询设计,弹出[显示表]窗口,点击[关闭]将该窗口关掉.这时软件会进入[设计]工具栏,我们点击工具栏左侧的[SQL视图].[SQL视图]默认选择的是[设计视图 ...

  10. Saber-图集

    PS:狙击手