我正在实现一种基于实体属性值的持久性机制。所有数据库访问都是通过休眠完成的。我有一个包含节点路径的表,它非常简单,只是一个 id,还有一个路径(字符串(路径数量很少,大约几千个。
主表有数百万行,我没有重复路径,而是规范化了它们自己表的路径。以下是我插入主表时想要的行为
1(检查路径表中是否存在路径(通过实体管理器查询,使用路径值作为参数(
2( 如果不存在,则插入并获取 ID(通过实体管理器保留(
3(将id作为外键值放入主表行,并将其插入主表。
对于一组域对象,这将发生数千次,这些域对象对应于主表和其他一些表中的许多行。因此,使用如下所示的单个事务重复上述步骤:
EntityTransaction t = entityManager.getTransaction();
t.begin();
//perform steps given above, check, and then persist etc..
t.commit();
当我执行步骤 2 时,它会给整个操作带来巨大的性能下降。它乞求缓存,因为一段时间后该表最多只有 10-20k 个条目,其中很少有新插入。我尝试用Hibernate这样做,但损失了将近2天。
我正在使用Hibernate 4.1,带有JPA注释和ECache。我尝试启用查询缓存,甚至在整个插入过程中使用相同的查询对象,如下所示:
Query call = entityManager.createQuery("select pt from NodePath pt " +
"where pt.path = :pathStr)");
call.setHint("org.hibernate.cacheable", true);
call.setParameter("pathStr", pPath);
List<NodePath> paths = call.getResultList();
if(paths.size() > 1)
throw new Exception("path table should have unique paths");
else if (paths.size() == 1){
NodePath path = paths.get(0);
return path.getId();
}
else {//paths null or has zero size
NodePath newPath = new NodePath();
newPath.setPath(pPath);
entityManager.persist(newPath);
return newPath.getId();
}
NodePath 实体的注释如下:
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "node_path", schema = "public")
public class NodePath implements java.io.Serializable {
据我从统计信息中看到,查询缓存正在使用中,但没有报告使用二级缓存:
queries executed to database=1
query cache puts=1
query cache hits=689
query cache misses=1
....
second level cache puts=0
second level cache hits=0
second level cache misses=0
entities loaded=1
....
一个简单的手写哈希表作为缓存,按预期工作,大大减少了总时间。我想由于我的操作性质,我无法触发休眠的缓存。
如何在此设置中使用休眠的二级缓存?作为记录,这是我的坚持xml:
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 版本="2.0">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>...</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
<property name="hibernate.connection.password" value="zyx" />
<property name="hibernate.connection.url" value="jdbc:postgresql://192.168.0.194:5432/testdbforml" />
<property name="hibernate.connection.username" value="postgres"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.search.autoregister_listeners" value="false"/>
<property name="hibernate.jdbc.batch_size" value="200"/>
<property name="hibernate.connection.autocommit" value="false"/>
<property name="hibernate.generate_statistics" value="true"/>
<property name="hibernate.cache.use_structured_entries" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
</properties>
好的,我找到了。我的问题是,缓存查询只在缓存中保留查询结果的 ID,并且(可能(返回到数据库以获取实际值,而不是从二级缓存中获取它们。
当然,问题在于,查询没有将这些值放入二级缓存,因为它们不是由主 id 选择的。因此,解决方案是使用一种将值放入二级缓存的方法,而在 hibernate 4.1 中,我已经设法使用自然 id 来做到这一点。这是从缓存中插入或返回值的函数,以防万一它对其他人有帮助:
private UUID persistPath(String pPath) throws Exception{
org.hibernate.Session session = (Session) entityManager.getDelegate();
NodePath np = (NodePath) session.byNaturalId(NodePath.class).using("path", pPath).load();
if(np != null)
return np.getId();
else {//no such path entry, so let's create one
NodePath newPath = new NodePath();
newPath.setPath(pPath);
entityManager.persist(newPath);
return newPath.getId();
}
}