多次调用时,Hibernate存储过程调用会挂起

时间:2017-02-06 19:12:01

标签: java mysql hibernate jpa stored-procedures

基本上我们有一堆数据库,然后是一个" 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;
    }
}

1 个答案:

答案 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编排多个尝试调用相同服务方法的线程。