基本上我们有一堆数据库,然后是一个" master"数据库,用于收集和计算一些统计信息。
然后我有一个可以调用的存储过程,以获取给定日期范围内的统计信息(然后我将其保存在我的数据库中)。
问题是,当我运行我的单元测试时,一切正常。对存储过程的单个调用将返回预期的数据。虽然当我尝试多次调用它时,fx与这个小黑客:
@Test
public void multipleRequests() {
StatisticManager statisticManager = new StatisticManager();
List<Errorstatistic> allStats = new ArrayList<>();
DateFormat format = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH);
for(int i = 1; i <= 31; i++){
String string = "01/" + i + "/2017";
Date date = null;
try {
date = format.parse(string);
} catch (ParseException e) {
System.out.println("Couldn't parse date.");
}
allStats.addAll(statisticManager.getNewStatistics(date));
}
statisticManager.persistNewStatistics(allStats);
}
然后只有第一个请求(01 / 01-2017)通过。然后我可以更改然后循环从2继续并重新启动它,2将通过但挂起3.快速sys-out调试显示,它在第二次尝试调用存储过程时挂起:
Get statistics called
Begin called
Returning em
feb. 06, 2017 7:52:34 PM org.hibernate.procedure.internal.ProcedureCallImpl prepareForNamedParameters
WARN: HHH000456: Named parameters are used for a callable statement, but database metadata indicates named parameters are not supported.
Getting list
Hibernate:
{call sp_errorratestat(?,?)}
Got list
Closing
Closed
Received data.
Get statistics called
Begin called
Returning em
Getting list
Hibernate:
{call sp_errorratestat(?,?)}
我似乎无法找出发生了什么。我试过去掉锁,插入最多8秒的睡眠时间,在循环中创建新的东西 - 但没有任何作用。
有人可以帮助我吗?
StatisticManager
public class StatisticManager {
ErrorStatisticProcedureDAO obiDao = new ErrorStatisticProcedureDAO();
ErrorStatisticDAO localDao = new ErrorStatisticDAO();
public void persistNewStatistics(List<Errorstatistic> errorstatistics) {
localDao.saveList(errorstatistics);
}
public List<Errorstatistic> getNewStatistics(Date from) {
List<Errorstatistic> originalStatistics = obiDao.getNewStatistics(setFrom(from), setTo(from));
System.out.println("Received data.");
return originalStatistics;
}
public List<Errorstatistic> getNewStatistics(Date from, Date to) {
List<Errorstatistic> originalStatistics = obiDao.getNewStatistics(setFrom(from), setTo(to));
System.out.println("Received data.");
return originalStatistics;
}
private Date setFrom(Date date){
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 00);
cal.set(Calendar.MINUTE, 00);
cal.set(Calendar.SECOND, 01);
return cal.getTime();
}
private Date setTo(Date date){
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
return cal.getTime();
}
}
DAO
public class ErrorStatisticProcedureDAO {
protected EntityManager em = ObiPersistenceManager.INSTANCE.getEntityManager();
private Object lock = new Object();
protected EntityManager begin() {
synchronized (lock) {
System.out.println("Begin called");
if (!em.getTransaction().isActive()) {
em.getTransaction().begin();
}
System.out.println("Returning em");
}
return em;
}
protected void close() {
synchronized (lock) {
System.out.println("Closing");
if (em.isOpen()) {
if (em.getTransaction().isActive()) {
em.getTransaction().commit();
em.clear();
}
System.out.println("Closed");
}
}
}
public List<Errorstatistic> getNewStatistics(Date from, Date to){
System.out.println("Get statistics called");
begin();
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("sp_errorratestat");
storedProcedure.registerStoredProcedureParameter("fromTs", Long.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("toTs", Long.class, ParameterMode.IN);
storedProcedure.setParameter("fromTs", (long) from.getTime() / 1000);
storedProcedure.setParameter("toTs", (long)to.getTime() / 1000);
System.out.println("Getting list");
List data = storedProcedure.getResultList();
System.out.println("Got list");
List<Errorstatistic> errorstatistics = new ArrayList<>();
Iterator itr = data.iterator();
while (itr.hasNext()) {
Object[] obj = (Object[]) itr.next();
Errorstatistic errorstatistic = new Errorstatistic();
errorstatistic.setDevicestate(obj[0] != null ? ((String) obj[0]) : null);
errorstatistic.setIftype(obj[1] != null ? (String) obj[1] : null);
errorstatistic.setServices(obj[2] != null ? (String) obj[2] : null);
errorstatistic.setHardware(obj[3] != null ? (String) obj[3] : null);
errorstatistic.setFirmware(obj[4] != null ? (String) obj[4] : null);
errorstatistic.setTicketstotal(obj[5] != null ? ((BigDecimal) obj[5]).intValueExact() : null);
errorstatistic.setBootstotal(obj[6] != null ? ((BigDecimal) obj[6]).intValueExact() : null);
errorstatistic.setCriticaldevices(obj[7] != null ? ((BigDecimal) obj[7]).intValueExact() : null);
errorstatistic.setUnstabledevices(obj[8] != null ? ((BigDecimal) obj[8]).intValueExact() : null);
errorstatistic.setDate(obj[9] != null ? (Date) obj[9] : null);
errorstatistic.setDevicetime(obj[11] != null ? ((BigDecimal) obj[11]).doubleValue() : null);
errorstatistic.setBootcausecountAdslFailure(obj[12] != null ? ((BigDecimal) obj[12]).intValueExact() : null);
errorstatistic.setBootcausecountPllLosinglock(obj[13] != null ? ((BigDecimal) obj[13]).intValueExact() : null);
errorstatistic.setBootcausecountPoweronHwreset(obj[14] != null ? ((BigDecimal) obj[14]).intValueExact() : null);
errorstatistic.setBootcausecountServerdown(obj[15] != null ? ((BigDecimal) obj[15]).intValueExact() : null);
errorstatistic.setBootcausecountSoftreset(obj[16] != null ? ((BigDecimal) obj[16]).intValueExact() : null);
errorstatistic.setBootcausecountUnknownreset(obj[17] != null ? ((BigDecimal) obj[17]).intValueExact() : null);
errorstatistic.setBootcausecountWatchdogreset(obj[18] != null ? ((BigDecimal) obj[18]).intValueExact() : null);
errorstatistic.setDevicecount(obj[19] != null ? ((BigInteger) obj[19]).longValue() : null);
errorstatistics.add(errorstatistic);
}
close();
return errorstatistics;
}
}
答案 0 :(得分:0)
从此代码段开始:
protected EntityManager begin() {
synchronized (lock) {
System.out.println("Begin called");
if (!em.getTransaction().isActive()) {
em.getTransaction().begin();
}
System.out.println("Returning em");
}
return em;
}
继续这个:
protected EntityManager em = ObiPersistenceManager.INSTANCE.getEntityManager();
我只能断定您使用的是单个EntityManager
,然后您需要在多个线程之间进行同步。否则,为什么需要同步块?
好吧,as stated in the Hibernate docs,持久化上下文不是线程安全的:
实现者不应该是线程安全的。相反,每一个 thread / transaction应该从a获取自己的实例 会话工厂。
Java EE和Spring都允许您通过EntityManager
注释注入@PersistenceContext
。这是一种非常好的做法,因为EntityManager
的生命周期绑定到当前事务边界。因此,每个服务层应用程序级事务都会获得一个事务上下文,而后者又使用它自己的EntityManager
局部变量。
如果您同时执行它,我怀疑您可以在单元测试中复制该问题。只需使用CountDownLatch
编排多个尝试调用相同服务方法的线程。