实现简单ORM案例
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案例的更多相关文章
- 简单登录案例(SharedPreferences存储账户信息)&联网请求图片并下载到SD卡(文件外部存储)
新人刚学习Android两周,写一个随笔算是对两周学习成果的巩固,不足之处欢迎各位建议和完善. 这次写的是一个简单登录案例,大概功能如下: 注册的账户信息用SharedPreferences存储: 登 ...
- MyBatis学习总结(一)简单入门案例
MyBatis学习总结(一)简单入门案例 主要内容:本文主要通过对数据库中的use表进行增删改查总结mybatis的环境搭建和基本入门使用 一.需要的jar包: 1.核心包 2.依赖包 3.jdbc数 ...
- 一个简单的案例带你入门Dubbo分布式框架
相信有很多小伙伴都知道,dubbo是一个分布式.高性能.透明化的RPC服务框架,提供服务自动注册.自动发现等高效服务治理方案,dubbo的中文文档也是非常全的,中文文档可以参考这里dubbo.io.由 ...
- solr简单搜索案例
solr简单搜索案例 使用Solr实现电商网站中商品信息搜索功能,可以根据关键字搜索商品信息,根据商品分类.价格过滤搜索结果,也可以根据价格进行排序,实现分页. 架构分为: 1. solr服务器 2. ...
- springcloud+eureka简单入门案例
springcloud+eureka简单入门案例 一.服务提供者 直接提供服务,入门案例没有特别要设置的地方,注意下端口,由于要启动多个服务,可能会冲突 配置文件(src/main/resources ...
- Python 简单爬虫案例
Python 简单爬虫案例 import requests url = "https://www.sogou.com/web" # 封装参数 wd = input('enter a ...
- arduino中SCoop库的简单应用案例
转载:https://www.csdn.net/gather_27/MtTaggzsMDExMS1ibG9n.html arduino中SCoop库的简单应用案例首先这篇文章来在视频https://v ...
- ReentrantReadWriteLock读写锁简单原理案例证明
ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...
- redux 的简单实用案例
redux 的简单实用案例 整体思想与结构 创建一个Action 创建一个Reducer 创建Store 在App组件开始使用 整体思想与结构 文件目录如下: 构建 action,通过创建一个函数,然 ...
随机推荐
- idea中使用Data Source and Drivers时,如果使用自己自定义的jar包
- 一起了解 .Net Foundation 项目 No.4
.Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. BenchmarkDotN ...
- js实现页面下拉后展示导航,以及点击导航自动滑动到相关页面
//监控,下拉750px后展示导航 $(window).scroll(function(){ var $this = $(this); var targetTop = $(this).scrollTo ...
- const在C与C++中的区别
在C中,const不是常量,只能说是一个不能改变的变量(注意是变量),C编译器不能把const看成看成一个编译期间的常量,因为他在内存中有分配,C编译器不知道他在编译期间的值.所以不能作为数组定义时的 ...
- ubuuntu截图
方法1: 按 print screen sysrq 方法2: 系统设置 选择键盘 选择快捷键窗口 选择截图 按照自己的习惯更改快捷键即可.
- 从相亲的角度理解 K8S 的 Node Affinity, Taints 与 Tolerations
这是昨天晚上阅读园子里的2篇 k8s 博文时产生的想法,在随笔中记录一下. 这2篇博文是 K8S调度之节点亲和性 与 K8S调度之Taints and Tolerations . 如果我们把 node ...
- X86现代汇编·02
下面4这个寄存器只能用16位或3N位名称访问3N位寄存器 :ESI EDI EBP ESP 16位寄存器: SI DI BP SP 特殊用法:乘除指令默认用EAX默认使用ECX为循环计数器ESP用于寻 ...
- vue生命周期钩子函数详解
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_35585701/article/ ...
- 基于XML装配bean的解析-Bean的作用域
一.Bean的种类1.普通bean:<bean id="" class="A"> ,spring直接创建A实例,并返回. 2.FactoryBe ...
- tableSizeFor()函数在java8和Java13的差别
java8 static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >&g ...