ORM定义:对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换
万物同源,始于JDBC
-
加载JDBC驱动程序
-
根据参数建立数据库连接
-
执行查询
-
处理结果集
-
处理异常,关闭连接
其他所有框架对关系数据库的操作都是基于以上步骤的封装。
基于JdbcTemplate实现简单的ORM,ORM的关注点是表跟实体的映射。那么关注点就在于
-
获取实体的定义
-
获取表的定义
-
实体跟表字段的一一映射
-
将对实体的操作转化为sql进而转化为对表的操作
简单InsertBean实现
方法定义
/**
* 往数据库指定的表插入一个BEAN的数据
* @param tableName
* @param keyId
* @param dataBean
* @return
*/
public Long insertBean(String tableName,String keyColumn,Object dataBean) throws DbException;
实现的模板方法
Map<String,Field> fieldMap = getClassFieldMap(bean.getClass());
List<String> fieldList = getTableFieldList(tableName);
Map<String,Object> dataMap = constructInsertMap(fieldMap, fieldList, keyColumn);
insertMap(dataMap,tableName)
insertMap(Map<String, ?> dataMap, String tableName) 具体实现
wrapInsertMap(dataMap); //对dataMap进行包装、再一次赋空值、日期处理等等
SimpleJdbcInsert insert = getSimpleJdbcInsert();
insert.withTableName(tableName).usingColumns(getCols(dataMap));
insert.execute(insertMap);
更直白的描述
InsertBeanActivity.png获取实体属性具体实现 getClassFieldMap(bean.getClass())
/**
* 取得类的属性名
* @param clazz
* @return
*/
public static Map<String,Field> getClassFieldMap(Class<?> clazz){
Map<String,Field> rtMap = null;
if(clazz != null){
if(null == rtMap){
rtMap = new HashMap<String,Field>();
Field[] fields = clazz.getDeclaredFields();
if(fields != null && fields.length>0){
Field.setAccessible(fields,true);
Field f = null;
for (int j = 0; j < fields.length; j++) {
f = fields[j];
rtMap.put(f.getName().toLowerCase(), f);
}
}
}
}
return rtMap;
}
获取表的字段具体实现(基于Oracle实现) getTableFieldList(tableName)
public List<String> getTableFieldList(String tableName){
List<String> list = null;
DbTable dbTable = getDbTableDefine(tableName);
if(dbTable != null && dbTable.getFieldMap() != null && !dbTable.getFieldMap().isEmpty()){
list = Arrays.asList(dbTable.getFieldMap().keySet().toArray(new String[dbTable.getFieldMap().keySet().size()]));
}
return list;
}
public DbTable getDbTableDefine(String tableName){
try {
return DbUtil.getDbTableDefine(tableName, this.getJdbcTemplate());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取Table的配置
* @param tableName
* @param conn
* @return
*/
public static DbTable getDbTableDefine(String tableName, JdbcTemplate jdbcTemplate){
if(StringUtils.isEmpty(tableName)){
return null;
}
WebRmInforesCacheService cacheService = SpringContextUtil.getBean("com.gpdi.infores.services.bs.WebRmInforesCacheServiceImpl");
tableName = tableName.toUpperCase();
DbTable dbTable = cacheService.getData("DbTable_"+tableName);
if(dbTable == null){
Connection conn = null;//DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
ResultSet rsTable = null;//取表
ResultSet rsField = null;//取字段
ResultSet rsKey = null;//取主键
ResultSet indexInfo = null;//索引
Statement stmt = null;//字段注释
ResultSet rsRemark = null;//字段注释
try {
conn = jdbcTemplate.getDataSource().getConnection();
DatabaseMetaData dm = conn.getMetaData();
String catalog = conn.getCatalog();
rsTable = dm.getTables(catalog, dm.getUserName(), tableName.toUpperCase(), new String[]{"TABLE", "SYNONYM"});
if(rsTable.next()) {
tableName = rsTable.getString("TABLE_NAME");
if(StringUtils.isNotEmpty(tableName)){
tableName = tableName.toUpperCase();
dbTable = new DbTable();
dbTable.setUserName(dm.getUserName());
dbTable.setTableName(tableName);
LinkedHashMap<String,DbTableField> fields = new LinkedHashMap<String,DbTableField>();
//取主键
rsKey = dm.getPrimaryKeys(catalog, null, tableName);
String keyField = null;
while (rsKey.next()) {
keyField = rsKey.getString("COLUMN_NAME");//组合组键时、只要最后一个主键
}
//取字段
rsField = dm.getColumns(catalog, null, tableName, null);
while (rsField.next()) {
DbTableField f = new DbTableField();
String fieldName = rsField.getString("COLUMN_NAME").toUpperCase();
f.setName(fieldName);//参数值可参考dm.getColumns(catalog, null, tableName, null)的帮助文档
f.setSqlType(new Integer(rsField.getString("DATA_TYPE")).intValue());//如:java.sql.Types.INTEGER
f.setTypeName(rsField.getString("TYPE_NAME"));//如:BIGINT
f.setSize(rsField.getString("COLUMN_SIZE"));//用户定义的字段长度
f.setBufferLength(rsField.getString("BUFFER_LENGTH"));//字段缓冲区大小
f.setDecimal(rsField.getString("DECIMAL_DIGITS"));//精度
f.setNullable("1".equals(rsField.getString("NULLABLE")));//取值0||1,1允许空值,0不允许空值
f.setDefaultValue(rsField.getString("COLUMN_DEF"));//默认值
f.setPosition(rsField.getString("ORDINAL_POSITION"));//在表中的位置
f.setRemark(rsField.getString("REMARKS"));//注释为空的、下面会有专门取注释
if(StringUtils.isNotEmpty(keyField) && keyField.equalsIgnoreCase(fieldName)){
f.setPrimarykey(true);
dbTable.setKeyField(f);//主键字段
}
fields.put(fieldName.toLowerCase(),f);
}
//字段注释
String remarkSQL = "select c.* from all_col_comments c where lower(c.table_name) = lower('"+tableName+"')";
if(StringUtils.isNotEmpty(dm.getUserName()))
remarkSQL += " and c.owner='"+dm.getUserName()+"'";
stmt = conn.createStatement();
rsRemark = stmt.executeQuery(remarkSQL);
while(rsRemark.next()){
String fieldNmae = rsRemark.getString("COLUMN_NAME");
fields.get(fieldNmae.toLowerCase()).setRemark(rsRemark.getString("COMMENTS"));
}
//表名注释
remarkSQL = "select c.* from all_tab_comments c where lower(c.table_name) = lower('"+tableName+"')";
if(StringUtils.isNotEmpty(dm.getUserName())) remarkSQL += " and c.owner='"+dm.getUserName()+"'";
ResultSet rstRemark = stmt.executeQuery(remarkSQL);
if(rstRemark.next()){
dbTable.setTableCnName(rstRemark.getString("COMMENTS"));
}
dbTable.setFieldMap(fields);
cacheService.cacheData("DbTable_"+tableName, dbTable);
}
}else{
dbTable = null;
}
} catch (Exception e) {
e.printStackTrace();
}finally{
close(rsRemark);
close(stmt);
close(indexInfo);
close(rsKey);
close(rsField);
close(rsTable);
close(conn);
}
}
return dbTable;
}
updateBean、deleteBean类似不再详述
selectBean
jdbcTemplate中实现了一个BeanPropertyRowMapper或者可以自己实现一个更加通用的RowMapper
好奇心害死猫--------------JdbcTemplate后面做了什么
几个重要的类与接口
- SimpleJdbcInsertOperations 定义了一些简单插入基本操作的接口
- AbstractJdbcInsert 抽象插入类、实现了几乎所有的插入相关操作
- SimpleJdbcInsert 插入操作的具体实现、简单将请求委托给父类
- TableMetaDataContext 保存与表相关的信息、提供一些插入构造参数的方法
关系------------只画出重要操作与属性
Relation.png流程
insert.withTableName(tableName).usingColumns(getCols(dataMap))
注册需要插入的表至TableMeteDataContext、插入字段则保存至AbstractJdbcInsert的declaredColumns中
insert.execute(insertMap)
插入具体流程
InsertFlow.png
重要方法
-
createInsertString
/** * Build the insert string based on configuration and metadata information * @return the insert string to be used */ public String createInsertString(String... generatedKeyNames) { Set<String> keys = new LinkedHashSet<String>(generatedKeyNames.length); for (String key : generatedKeyNames) { keys.add(key.toUpperCase()); } StringBuilder insertStatement = new StringBuilder(); insertStatement.append("INSERT INTO "); if (getSchemaName() != null) { insertStatement.append(getSchemaName()); insertStatement.append("."); } insertStatement.append(getTableName()); insertStatement.append(" ("); int columnCount = 0; for (String columnName : getTableColumns()) { if (!keys.contains(columnName.toUpperCase())) { columnCount++; if (columnCount > 1) { insertStatement.append(", "); } insertStatement.append(columnName); } } insertStatement.append(") VALUES("); if (columnCount < 1) { if (this.generatedKeyColumnsUsed) { logger.info("Unable to locate non-key columns for table '" + getTableName() + "' so an empty insert statement is generated"); } else { throw new InvalidDataAccessApiUsageException("Unable to locate columns for table '" + getTableName() + "' so an insert statement can't be generated"); } } for (int i = 0; i < columnCount; i++) { if (i > 0) { insertStatement.append(", "); } insertStatement.append("?"); } insertStatement.append(")"); return insertStatement.toString(); }
-
createInsertTypes 不再贴出源码
-
executeInsertInternal
if (logger.isDebugEnabled()) { logger.debug("The following parameters are used for insert " + getInsertString() + " with: " + values); } return getJdbcTemplate().update(getInsertString(), values.toArray(), getInsertTypes());
update回到JdbcTemplate类一路封装最后执行public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)方法
其他一些有意思的东西
- 更大的自由度---定义表与实体类、表字段与实体类属性对应关系。Hibernate的Implicit Naming Strategy与Physical Naming Strategy
- 将sql转移到别的地方。Mybatis的xml,Hibernate的基于注解的NamedQueries
- 基于方法名生成简单的sql。类似JPA 或者像我们系统根据自定义表单、表格生成稍微复杂的自定义sql
- 将表定义、执行相关信息等进行缓存。一级二级缓存。
其他
- ORM论战------是否应使用ORM
网友评论