在hibernate中,把从数据库中查询到的一条记录拷贝到一个是实例,是采用反射的方式来完成的。其流程大致这样的:
//用户实体类,在后面的例子使用
public Class User{
private int autoId;
private String name;
public int getAutoId(){
return autoId;
}
public String getName(){
return name;
Class<?> entityClass = Class.forName("User");
while(rs.next()){
Object entity = entityClass.newInstance();
//获取中实体的set方法,获取次方已经被缓存,通过缓存来查询的,此处只是用来说明问题
Method m = entityClass.getMethod("setName");
m.invoke(entity,rs.getString("name"));
m = entityClass.getMethod("setAutoId");
m.invoke(entity,rs.getInt("AutoId"));
}
我不知道oracle,bea和sun的ORM(简称,后面都会这样称呼)是不是也是这样实现,但是我知道我们公司早先的一款ORM和现在的一款ORM都是这样的方案来实现的。
【问题】
众所周知,在java中,通过反射调用方法相当的消耗资源,而且速度比起直接执行原方法要慢出好多,所以在hibernate中,如果大量的对象被频繁的创建时,在这一块性能是有所下降的,尤其是在高速运行的系统中。这就出现了,有人宁愿直接使用JDBC,也不使用hibernate的现象。
【突发奇想】
昨天在回家的公交车上,突然想到了早先我写的一个ORM。
两年前,那时我还在毕业后的第一家公司任职,我在非上班的时间写了一个非常简单的ORM(和iBATIS相像,离开那家公司后我才知道iBATIS,不过在写那个ORM时,我在网上搜寻过,没有找到合适我们那个报表系统的ORM,更没有看到iBATIS,就是iBATIS也不完全适合),后来我把它直接用到了公司的报表系统中,大家也方便了不少。其中有个CallBack类,其结构大致如下:
public interface ResultSetCallBack{
public Object readEntity(ResultSet rs)throws SQLException;
}
使用人是只要实现这个接口就行了,以User为例,应该是这样的:
//当时我们的数据不同步,所以没有缓存
public UserCallBack implements ResultSetCallBack{
public Object readEntity(ResultSet rs)throws SQLException{
User user = new User();
user.setAutoId(rs.getInteger("autoId"));
user.setName(rs.getString("name"));
return object;
}
}
【想法】
如果把hibernate中“数据拷贝到实例”的方式改用ResultSetCallBack的方式,那hibernate在这一块的性能肯定会有所提高(不知道这一块的性能在整个性能中占据的地位)。
【实现方式】
ResultSetCallBack所有的实现类都使用动态生成的方法来完成,至于工具可以采用ASM或者javassit。
【我的实现】
至于怎么实现的"ResultSetCallBack实现类"的动态生成,昨天晚上在家我基本上写好了一个。贴出来主要的部分,其他的可以看附件:
//EntitySetter.java
//CallBack类
public interface EntitySetter {
public void setEntityProperties(Object object,ResultSet rs) throws SQLException;
}
//JsEntitySetterGenerator.java
//ResultSetCallBack实现类的动态生成类
public class JsEntitySetterGenerator {
private final static ClassPool pool = ClassPool.getDefault();
static{
pool.importPackage("java.lang");
pool.importPackage("java.sql");
pool.importPackage("java.math");
pool.importPackage("java.util");
}
private EntityMapping entityMapping;
public JsEntitySetterGenerator(EntityMapping em) {
super();
this.entityMapping = em;
}
public byte[] generateEntitySetter() throws CannotCompileException,IOException {
CtClass c = generateEntitySetter0();
return c.toBytecode();
}
public Class<?> generateEntitySetter(ClassLoader loader) throws CannotCompileException,IOException {
CtClass c = generateEntitySetter0();
return c.toClass(loader, null);
}
private CtClass generateEntitySetter0() throws CannotCompileException{
String now = Long.toHexString(System.currentTimeMillis());
String className = "com/azk/entity/Z_" + now + "_"
+ entityMapping.getTableName();
CtClass c = pool.makeClass(className);
List<EntityFieldMapping> list = entityMapping.getFieldMappingList();
String entityClassInternalName = entityMapping.getClass().getName();
StringBuffer src = new StringBuffer();
src.append("public void setEntityProperties(Object obj,ResultSet rs)");
src.append(" throws SQLException{");
src.append(entityClassInternalName).append(" entity = (").append(
entityClassInternalName).append("obj);");
for (EntityFieldMapping mapping : list) {
generateSetPropertySource(src, mapping);
}
src.append("}");
CtMethod method = CtMethod.make(src.toString(), c);
c.addMethod(method);
return c;
}
private void generateSetPropertySource(StringBuffer src,
EntityFieldMapping mapping) {
ResultSetSupportedDateType dt = mapping.getDateType();
if (ResultSetSupportedDateType.Byte == dt) {
visitPrimitive(src, mapping, Byte.class);
} else if (ResultSetSupportedDateType.Short == dt) {
visitPrimitive(src, mapping, Short.class);
} else if (ResultSetSupportedDateType.Int == dt) {
visitPrimitive(src, mapping, Integer.class);
} else if (ResultSetSupportedDateType.Long == dt) {
visitPrimitive(src, mapping, Long.class);
} else if (ResultSetSupportedDateType.Float == dt) {
visitPrimitive(src, mapping, Float.class);
} else if (ResultSetSupportedDateType.Double == dt) {
visitPrimitive(src, mapping, Double.class);
} else if (ResultSetSupportedDateType.Date == dt
|| ResultSetSupportedDateType.String == dt
|| ResultSetSupportedDateType.Timestamp == dt) {
visitSimpleNonePrimitive(src, mapping);
} else if (ResultSetSupportedDateType.Blob == dt) {
visitBlobMethod(src, mapping);
} else if (ResultSetSupportedDateType.Clob == dt) {
visitClobMethod(src, mapping);
}
}
private void visitSimpleNonePrimitive(StringBuffer src,
EntityFieldMapping mapping) {
String resultSetMethod = mapping.getDateType().getResultSetMethodName();
String propertyName = mapping.getPropertyName();
EntitySetterMethodInfo inf = buildSetterInfo(propertyName);
String fieldName = mapping.getTableFieldName();
src.append("entity.").append(inf.getMethodName()).append("(");
src.append("rs.").append(resultSetMethod).append("(\"").append(
fieldName).append("\")");
src.append(");");
}
private void visitPrimitive(StringBuffer src, EntityFieldMapping mapping,
Class<?> btAd) {
String resultSetMethod = mapping.getDateType().getResultSetMethodName();
String propertyName = mapping.getPropertyName();
EntitySetterMethodInfo inf = buildSetterInfo(propertyName);
String fieldName = mapping.getTableFieldName();
// entity.setAge(
src.append("entity.").append(inf.getMethodName()).append("(");
if (inf.getParameterType() == btAd) {
// new Integer(
src.append("new ").append(btAd.getName()).append("(");
// rs.getInt("age")
src.append("rs.").append(resultSetMethod).append("(\"").append(
fieldName).append("\")");
// )
src.append(")");
} else {
// rs.getInt("age")
src.append("rs.").append(resultSetMethod).append("(\"").append(
fieldName).append("\")");
}
// );
src.append(");");
}
protected void visitBlobMethod(StringBuffer src, EntityFieldMapping mapping) {
throw new UnsupportedDataType();
}
protected void visitClobMethod(StringBuffer src, EntityFieldMapping mapping) {
throw new UnsupportedDataType();
}
private EntitySetterMethodInfo buildSetterInfo(String propertyName) {
String fc = propertyName.substring(0, 1).toUpperCase();
String methodName = "set" + fc + propertyName.substring(1);
Method[] ms = entityMapping.getEntityClass().getMethods();
for (Method method : ms) {
if (method.getName().equals(methodName)) {
Class<?>[] paramType = method.getParameterTypes();
if (paramType.length == 1) {
EntitySetterMethodInfo inf = new EntitySetterMethodInfo();
inf.setDesc(Type.getMethodDescriptor(method));
inf.setParameterType(paramType[0]);
inf.setMethodName(methodName);
return inf;
}
}
}
return null;
}
protected class EntitySetterMethodInfo {
private String desc;
private Class<?> parameterType;
private String methodName;
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Class<?> getParameterType() {
return parameterType;
}
public void setParameterType(Class<?> parameterType) {
this.parameterType = parameterType;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
}
}
package com.azk.orm;
public class ResultSetSupportedDateType {
public static final ResultSetSupportedDateType Byte = new ResultSetSupportedDateType(
"B", "getByte");
public static final ResultSetSupportedDateType Short = new ResultSetSupportedDateType(
"S", "getShort");
public static final ResultSetSupportedDateType Int = new ResultSetSupportedDateType(
"I", "getInt");
public static final ResultSetSupportedDateType Long = new ResultSetSupportedDateType(
"J", "getLong");
public static final ResultSetSupportedDateType Float = new ResultSetSupportedDateType(
"F", "getFloat");
public static final ResultSetSupportedDateType Double = new ResultSetSupportedDateType(
"D", "getDoucle");
public static final ResultSetSupportedDateType String = new ResultSetSupportedDateType(
"java/lang/String", "getInt");
public static final ResultSetSupportedDateType Date = new ResultSetSupportedDateType(
"java/sql/Date", "getInt");
public static final ResultSetSupportedDateType Timestamp = new ResultSetSupportedDateType(
"java/sql/Timestamp", "getTimestamp");
public static final ResultSetSupportedDateType BigDecimal = new ResultSetSupportedDateType(
"java/math/BigDecimal", "getBigDecimal");
public static final ResultSetSupportedDateType Clob = new ResultSetSupportedDateType(
"java/sql/Clob", "getClob");
public static final ResultSetSupportedDateType Blob = new ResultSetSupportedDateType(
"java/sql/Blob", "getBlob");
private String typeClassName;
private String resultSetMethodName;
private ResultSetSupportedDateType(String typeName,
String resultSetMethodName) {
this.typeClassName = typeName;
this.resultSetMethodName = resultSetMethodName;
}
public String getTypeClassName() {
return typeClassName;
}
public void setTypeClassName(String typeClassName) {
this.typeClassName = typeClassName;
}
public String getResultSetMethodName() {
return resultSetMethodName;
}
public void setResultSetMethodName(String resultSetMethodName) {
this.resultSetMethodName = resultSetMethodName;
}
}
分享到:
- 2007-01-12 16:53
- 浏览 4758
- 评论(0)
- 论坛回复 / 浏览 (0 / 3350)
- 查看更多
相关推荐
3. **游离状态(Detached)**:持久化对象在某些情况下可能会从Session的缓存中移除,但仍与数据库中的一条记录相对应,这时它就变成了游离对象。游离对象需要手动管理,以确保数据同步。 对象状态的转换主要由...
2. **MyBatis分页**:MyBatis是一个流行的持久层框架,它允许在Mapper XML文件或注解中直接写SQL,支持动态SQL,分页插件如PageHelper可以简化分页逻辑。配置好插件后,只需在查询方法上添加Page对象,MyBatis会自动...
3. 持久态(Persistent):对象已通过Session保存,与数据库中的一条记录对应。一旦对象变为持久态,Hibernate会负责它的生命周期管理,包括插入、更新和删除操作。 二、一对一(OneToOne)关系映射 在数据库设计...
- **代码复用**:可以通过一个存储过程调用另一个存储过程,简化复杂的操作流程。 - **减少网络流量**:存储过程可以执行一系列SQL语句,减少了与数据库交互的次数,从而降低了网络开销。 - **安全性增强**:存储...
每个实体变为一个表,每条关系变为表间的关系。例如,“学生”实体对应“Student”表,“教师”对应“Teacher”表,“课程”对应“Course”表,“学生选课”关系可以转化为“Enrollment”表,包含学生学号和课程号...
持久态对象拥有唯一的数据库标识,并且与数据库中的一条记录相对应。在Session的生命周期内,对持久态对象的修改会被跟踪,当调用`flush()`方法时,这些更改会被同步到数据库。如果Session关闭,再打开新的Session,...
- Hibernate保证每个Session实例的缓存中,每条数据库记录仅对应一个持久化对象。这意味着在不同Session实例中,相同数据库记录可能会有不同的持久化对象实例。 - 如果试图让一个Java对象同时被两个Session实例...
- 数据库记录的主键:UUID可以作为数据库表记录的主键,保证每条记录的唯一性,尤其在分布式数据库系统中。 - 临时ID:在需要临时唯一标识的场景下,UUID是一个好选择,因为它不会与其他系统产生的ID冲突。 - ...
- 一对多(OneToMany):一个实体对应数据库表中的多条记录,通常使用Set或List集合存储。 - 多对多(ManyToMany):两个实体之间有多对多关系,通常通过中间表来实现。 了解这些核心概念对于理解和使用Hibernate...
此时,该对象仅仅是一个普通的Java Bean,并没有与数据库建立关联。 2. **托管状态(Managed State)** 一旦瞬时状态的对象通过`persist()`方法被实体管理器管理后,该对象转变为托管状态。在此状态下,实体管理...
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 - **适用场景**: - 有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。 - 在不明确指定接收者的情况下,向多个对象...
例如,如果你试图更新一个已存在于数据库中的对象,但该对象的ID未知或已被错误地设置,`saveOrUpdate`可能会意外地插入一条新记录而不是更新原有记录。因此,在使用`saveOrUpdate`时,需确保对象的状态和标识符的...
在Java编程中,分页是数据管理中一个重要的概念,特别是在处理大数据集合时,它可以有效地减少内存消耗并提高用户体验。本项目提供了一套完整的Java分页源代码,包含与数据库交互的部分,使得开发者可以直接运行并...
ORM:使用元数据信息来描述对象和数据库之间的关系,并且能够自动实现java中持久化对象到关系型数据库中表的映射 脏检查:自动对缓存中的数据进行检查,并且选择在合适的时机和数据库之间进行交互,以保持数据的...
当 `COLUMN_ID` 为 1 时,表示第一条记录,此时返回表的注释;否则返回空字符串。 - 使用了多个 **JOIN** 来关联不同的视图: - `user_tables` 与 `user_tab_columns` 联接,获取表及其列信息。 - `user_tab_...
Q0030 在数据库中什么代表一条记录? 主健 Q0031 如何编写效率高的SQL语句? "1.根据查询条件建立合适的index 2.因为SQL是从右向左解析,多表查询时,记录数少的表放在右边 3.多个条件时,收敛快的条件放在右边。 4...
3. **游离状态(Detached)**:游离状态的实体对象已经离开了Session的管理,但它的主键ID仍然与数据库中的一条记录对应。这意味着对象与数据库有连接,但当前Session不知道这个对象。游离状态的对象可以通过Session的...
它们代表了数据库中的一条记录,但不再受Hibernate的管理。 2. **merge()操作**: - 当你对一个游离态对象调用`merge()`时,Hibernate首先检查该对象的ID。如果ID存在,那么: - 如果数据库中找不到匹配的ID,`...
例如,插入一条记录: ```java String sql = "INSERT INTO table (column1, column2) VALUES (?, ?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, value1); pstmt.setString(2, value2); pstmt...