JPA-Eclipselink 缓存嵌套表(实体)
JPA-Eclipselink caching nested tables (entities)
我正在结合使用 Java JPA-Eclipselink 持久性 (v2.6.9) 和 Apache Tomcat Web 服务器。我在每次调用 Web 服务时从嵌套表中从数据库中获取大量数据。数据库中的数据不断地被新数据扩展。
我们预测提取次数将会增加,因此我们 运行 压力测试发现预期的流量将导致数据库堵塞。为了避免堵塞,我想以eclipse link 应该获取数据并在缓存中保存一分钟并在一分钟后获取新数据的方式实现临时缓存。如果我理解正确的话,我正在 Eclipselink 中寻找 L2 缓存。
我已经尝试了几种选择,但 eclipse link 总是在每次调用 Web 服务方法时从数据库中获取数据。
你能帮我让eclipselink每隔60秒使用缓存吗?
请在下面找到我的尝试。
简化实体
@Entity
@Cache(
type=CacheType.SOFT, // Cache everything until the JVM decides memory is low.
size=64000, // Use 64,000 as the initial cache size.
expiry=36000000 // 10 minutes
)
@Table(name="SITUATION_DATA")
@NamedQuery(name="SituationData.findAll", query="SELECT s FROM SituationData s")
public class DatexSituationData implements Serializable {
private static final long serialVersionUID = 1L;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="datexSituationData", cascade = CascadeType.PERSIST, fetch =
FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecord> situationRecords;
}
@Entity
@Table(name="SituationRecord")
@NamedQuery(name="SituationRecord.findAll", query="SELECT s FROM SituationRecord s")
public class SituationRecord implements Serializable {
private static final long serialVersionUID = 1L;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordComment> situationRecordComment;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordTypeElement> situationRecordTypeElements;
//bi-directional many-to-one association to SituationLocation
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
@JoinColumn(name="ID_LOKACIJE")
private SituationLocation situationLocation;
//bi-directional many-to-one association to DatexSituationData
@ManyToOne()
@JoinColumns({
@JoinColumn(name="SITUATION_ID", referencedColumnName="ID", nullable=false),
@JoinColumn(name="SITUATION_VERSION", referencedColumnName="VERSION", nullable=false)
})
private DatexSituationData datexSituationData;
}
@Entity
@Table(name="SITUATION_LOCATIONS")
@NamedQuery(name="SituationLocation.findAll", query="SELECT s FROM SituationLocation s")
public class SituationLocation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
GeneratedValue(strategy=GenerationType.SEQUENCE, generator="situation_location_seq")
@SequenceGenerator(name="situation_location_seq", sequenceName="SEQ_SITUATION_LOCATION",
allocationSize=1)
@Column(name="ID_LOKACIJE", unique=true, nullable=false)
private long idLokacije;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="situationLocation", cascade = CascadeType.PERSIST)
private List<SituationRecord> situationRecords;
}
正在从数据库中获取
String sQuery =
"SELECT ds FROM SituationData ds where CONCAT(ds.id.version, ds.id.id) in (select
CONCAT(max(FUNC('TO_NUMBER', ds1.id.version)), ds1.id.id) from SituationData ds1 group by
ds1.id.id)";
EntityManager em = Emf.getInstance().getFactory().createEntityManager();
Query q = em.createQuery(sQuery, DatexSituationData.class);
q.setHint("eclipselink.join-fetch", "ds.situationRecords");
q.setHint("eclipselink.join-fetch", "ds.situationRecords.situationLocation");
q.setHint("eclipselink.join-fetch", "ds.situationRecords.situationRecordTypeElements");
q.setHint("eclipselink.join-fetch",
"ds.situationRecords.situationRecordGeneralPublicCommentMappings.
situationRecordGeneralPublicComments");
q.setHint("eclipselink.sql.hint", "/*+ leading(t1 t0) */");
q.setHint("eclipselink.query-results-cache", "true");
q.setHint("eclipselink.query-results-cache.expiry", "120000");
lResult = q.getResultList();
Persistence.xml
我试过以下属性
<!--property name="eclipselink.query-results-cache" value="true"/>
<property name="eclipselink.query-results-cache.expiry" value="120000"/>
<property name="eclipselink.cache.shared.default" value="true"/-->
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.shared.SituationData" value="true"/>
<property name="eclipselink.cache.type.SituationData" value="SOFT"/>
<property name="eclipselink.cache.size.SituationData" value="9400000"/>
您对实体所做的设置用于实体缓存,但复杂的查询仍然需要使用数据库来确定哪些实体匹配读取 all/list 查询。尽管多次执行此查询将使用该实体缓存,但您仍然应该看到改进 - 虽然数据仍然来自数据库 'fetched',但您正在避免 building/refreshing 实例的开销。更好的测试可能是检查涉及的开销:
EntityManager em1 = Emf.getInstance().getFactory().createEntityManager();
EntityManager em2 = Emf.getInstance().getFactory().createEntityManager();
DatexSituationData data1 = em1.find(DatexSituationData.class, 1L);
DatexSituationData data2 = em2.find(DatexSituationData.class, 1L);
第一个查找将转到数据库并创建实例来填充共享和本地缓存,而第二个查找应该只使用共享缓存为其本地 EntityManager 缓存创建一个副本。这是一项测试,显示您的共享缓存及其设置是否在没有其他复杂性的情况下正常工作。
您的设置和显示的内容存在一些问题:
- 实体 class(和名称)是“DatexSituationData”,而您使用“SituationData”命名查询
- "eclipselink.cache.shared.SituationData" 再次用于 SituationData
- "eclipselink.cache.shared.default" 是错误的。这会关闭所有实体的缓存,这会在您的应用程序中造成一些混乱,因为 SituationData/DatexSituationData 可能会在共享缓存中被引用,但其引用的 SituationRecords 不会强制加载数据库命中。
您还设置了“query-results-cache”选项作为传递给查询的查询提示。 docs 中对此进行了描述,这也是您可能需要的,因为它将允许根据所使用的参数缓存查询结果。文档的限制:
Only named queries can have their results cached, dynamic queries
cannot use the query results cache. As well, if you modify a named
query before execution, such as setting hints or properties, then it
cannot use the cached results.
因此,要测试查询缓存,您应该仅使用命名查询且不做任何更改:
EntityManager em1 = Emf.getInstance().getFactory().createEntityManager();
Query namedQuery = em.createNamedQuery("DatexSituationData.findAll", DatexSituationData.class);
namedQuery.getResultList();
em.clear();
namedQuery = em.createNamedQuery("DatexSituationData.findAll", DatexSituationData.class);
namedQuery.getResultList();
如果您需要添加查询提示,例如提取连接和查询结果缓存本身,请将它们添加到查询定义中:
@NamedQuery(name="DatexSituationData.complexFind",
query="SELECT ds fetch join ds.situationRecords FROM DatexSituationData ds where CONCAT(ds.id.version, ds.id.id) in (select
CONCAT(max(FUNC('TO_NUMBER', ds1.id.version)), ds1.id.id) from SituationData ds1 group by
ds1.id.id)",
hints={@QueryHint(name="eclipselink.join-fetch", value="ds.situationRecords.situationLocation"),
@QueryHint(name="eclipselink.join-fetch", value="ds.situationRecords.situationRecordTypeElements"),
@QueryHint(name="eclipselink.join-fetch", value="ds.situationRecords.situationRecordGeneralPublicCommentMappings.situationRecordGeneralPublicComments"),
@QueryHint(name="eclipselink.sql.hint", value="/*+ leading(t1 t0) */"),
@QueryHint(name="eclipselink.query-results-cache", value="true"),
@QueryHint(name="eclipselink.query-results-cache.expiry", value="120000")})//2 minutes
或在运行时间添加命名查询:
Query namedQueryToSave = em.createQuery(yourQueryString, DatexSituationData.class);
//add all hints
em.getEntityManagerFactory().addNamedQuery("DatexSituationData.complexFind", namedQueryToSave);
em.close();
无论哪种方式,您都可以执行查询。在使用“DatexSituationData.complexFind”的单个 em 中使用之前的测试应该会在第一个 运行 上显示对数据库的命中,但不会在第二个上显示对数据库的命中 - 只要它们相隔不到 2 分钟。
我正在结合使用 Java JPA-Eclipselink 持久性 (v2.6.9) 和 Apache Tomcat Web 服务器。我在每次调用 Web 服务时从嵌套表中从数据库中获取大量数据。数据库中的数据不断地被新数据扩展。 我们预测提取次数将会增加,因此我们 运行 压力测试发现预期的流量将导致数据库堵塞。为了避免堵塞,我想以eclipse link 应该获取数据并在缓存中保存一分钟并在一分钟后获取新数据的方式实现临时缓存。如果我理解正确的话,我正在 Eclipselink 中寻找 L2 缓存。 我已经尝试了几种选择,但 eclipse link 总是在每次调用 Web 服务方法时从数据库中获取数据。
你能帮我让eclipselink每隔60秒使用缓存吗?
请在下面找到我的尝试。
简化实体
@Entity
@Cache(
type=CacheType.SOFT, // Cache everything until the JVM decides memory is low.
size=64000, // Use 64,000 as the initial cache size.
expiry=36000000 // 10 minutes
)
@Table(name="SITUATION_DATA")
@NamedQuery(name="SituationData.findAll", query="SELECT s FROM SituationData s")
public class DatexSituationData implements Serializable {
private static final long serialVersionUID = 1L;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="datexSituationData", cascade = CascadeType.PERSIST, fetch =
FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecord> situationRecords;
}
@Entity
@Table(name="SituationRecord")
@NamedQuery(name="SituationRecord.findAll", query="SELECT s FROM SituationRecord s")
public class SituationRecord implements Serializable {
private static final long serialVersionUID = 1L;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordComment> situationRecordComment;
@OneToMany(mappedBy="situationRecord", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
private List<SituationRecordTypeElement> situationRecordTypeElements;
//bi-directional many-to-one association to SituationLocation
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinFetch(value=JoinFetchType.OUTER)
@JoinColumn(name="ID_LOKACIJE")
private SituationLocation situationLocation;
//bi-directional many-to-one association to DatexSituationData
@ManyToOne()
@JoinColumns({
@JoinColumn(name="SITUATION_ID", referencedColumnName="ID", nullable=false),
@JoinColumn(name="SITUATION_VERSION", referencedColumnName="VERSION", nullable=false)
})
private DatexSituationData datexSituationData;
}
@Entity
@Table(name="SITUATION_LOCATIONS")
@NamedQuery(name="SituationLocation.findAll", query="SELECT s FROM SituationLocation s")
public class SituationLocation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
GeneratedValue(strategy=GenerationType.SEQUENCE, generator="situation_location_seq")
@SequenceGenerator(name="situation_location_seq", sequenceName="SEQ_SITUATION_LOCATION",
allocationSize=1)
@Column(name="ID_LOKACIJE", unique=true, nullable=false)
private long idLokacije;
//bi-directional many-to-one association to SituationRecord
@OneToMany(mappedBy="situationLocation", cascade = CascadeType.PERSIST)
private List<SituationRecord> situationRecords;
}
正在从数据库中获取
String sQuery =
"SELECT ds FROM SituationData ds where CONCAT(ds.id.version, ds.id.id) in (select
CONCAT(max(FUNC('TO_NUMBER', ds1.id.version)), ds1.id.id) from SituationData ds1 group by
ds1.id.id)";
EntityManager em = Emf.getInstance().getFactory().createEntityManager();
Query q = em.createQuery(sQuery, DatexSituationData.class);
q.setHint("eclipselink.join-fetch", "ds.situationRecords");
q.setHint("eclipselink.join-fetch", "ds.situationRecords.situationLocation");
q.setHint("eclipselink.join-fetch", "ds.situationRecords.situationRecordTypeElements");
q.setHint("eclipselink.join-fetch",
"ds.situationRecords.situationRecordGeneralPublicCommentMappings.
situationRecordGeneralPublicComments");
q.setHint("eclipselink.sql.hint", "/*+ leading(t1 t0) */");
q.setHint("eclipselink.query-results-cache", "true");
q.setHint("eclipselink.query-results-cache.expiry", "120000");
lResult = q.getResultList();
Persistence.xml
我试过以下属性
<!--property name="eclipselink.query-results-cache" value="true"/>
<property name="eclipselink.query-results-cache.expiry" value="120000"/>
<property name="eclipselink.cache.shared.default" value="true"/-->
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.shared.SituationData" value="true"/>
<property name="eclipselink.cache.type.SituationData" value="SOFT"/>
<property name="eclipselink.cache.size.SituationData" value="9400000"/>
您对实体所做的设置用于实体缓存,但复杂的查询仍然需要使用数据库来确定哪些实体匹配读取 all/list 查询。尽管多次执行此查询将使用该实体缓存,但您仍然应该看到改进 - 虽然数据仍然来自数据库 'fetched',但您正在避免 building/refreshing 实例的开销。更好的测试可能是检查涉及的开销:
EntityManager em1 = Emf.getInstance().getFactory().createEntityManager();
EntityManager em2 = Emf.getInstance().getFactory().createEntityManager();
DatexSituationData data1 = em1.find(DatexSituationData.class, 1L);
DatexSituationData data2 = em2.find(DatexSituationData.class, 1L);
第一个查找将转到数据库并创建实例来填充共享和本地缓存,而第二个查找应该只使用共享缓存为其本地 EntityManager 缓存创建一个副本。这是一项测试,显示您的共享缓存及其设置是否在没有其他复杂性的情况下正常工作。
您的设置和显示的内容存在一些问题:
- 实体 class(和名称)是“DatexSituationData”,而您使用“SituationData”命名查询
- "eclipselink.cache.shared.SituationData" 再次用于 SituationData
- "eclipselink.cache.shared.default" 是错误的。这会关闭所有实体的缓存,这会在您的应用程序中造成一些混乱,因为 SituationData/DatexSituationData 可能会在共享缓存中被引用,但其引用的 SituationRecords 不会强制加载数据库命中。
您还设置了“query-results-cache”选项作为传递给查询的查询提示。 docs 中对此进行了描述,这也是您可能需要的,因为它将允许根据所使用的参数缓存查询结果。文档的限制:
Only named queries can have their results cached, dynamic queries cannot use the query results cache. As well, if you modify a named query before execution, such as setting hints or properties, then it cannot use the cached results.
因此,要测试查询缓存,您应该仅使用命名查询且不做任何更改:
EntityManager em1 = Emf.getInstance().getFactory().createEntityManager();
Query namedQuery = em.createNamedQuery("DatexSituationData.findAll", DatexSituationData.class);
namedQuery.getResultList();
em.clear();
namedQuery = em.createNamedQuery("DatexSituationData.findAll", DatexSituationData.class);
namedQuery.getResultList();
如果您需要添加查询提示,例如提取连接和查询结果缓存本身,请将它们添加到查询定义中:
@NamedQuery(name="DatexSituationData.complexFind",
query="SELECT ds fetch join ds.situationRecords FROM DatexSituationData ds where CONCAT(ds.id.version, ds.id.id) in (select
CONCAT(max(FUNC('TO_NUMBER', ds1.id.version)), ds1.id.id) from SituationData ds1 group by
ds1.id.id)",
hints={@QueryHint(name="eclipselink.join-fetch", value="ds.situationRecords.situationLocation"),
@QueryHint(name="eclipselink.join-fetch", value="ds.situationRecords.situationRecordTypeElements"),
@QueryHint(name="eclipselink.join-fetch", value="ds.situationRecords.situationRecordGeneralPublicCommentMappings.situationRecordGeneralPublicComments"),
@QueryHint(name="eclipselink.sql.hint", value="/*+ leading(t1 t0) */"),
@QueryHint(name="eclipselink.query-results-cache", value="true"),
@QueryHint(name="eclipselink.query-results-cache.expiry", value="120000")})//2 minutes
或在运行时间添加命名查询:
Query namedQueryToSave = em.createQuery(yourQueryString, DatexSituationData.class);
//add all hints
em.getEntityManagerFactory().addNamedQuery("DatexSituationData.complexFind", namedQueryToSave);
em.close();
无论哪种方式,您都可以执行查询。在使用“DatexSituationData.complexFind”的单个 em 中使用之前的测试应该会在第一个 运行 上显示对数据库的命中,但不会在第二个上显示对数据库的命中 - 只要它们相隔不到 2 分钟。