Проблема транзакции в Java с Hibernate - последние записи не извлекаются из базы данных - PullRequest
1 голос
/ 19 марта 2010

У меня возникла проблема с транзакциями в моем приложении. Я использую Java 1.6 и Hibernate 3.2.5.

Мое приложение запускает ежемесячный процесс, в котором создаются записи для каждого пользователя в базе данных на основе их ежемесячной активности. Эти платежные записи затем используются для создания объекта Ежемесячный счет. Процесс такой:

  1. Получить пользователей, которые имели активность в прошлом месяце
  2. Создание соответствующих платежных записей для каждого пользователя
  3. Получите набор платежных записей, которые мы только что создали
  4. Создание ежемесячного счета на основе этих записей

Все отлично работает до шага 3 выше. Записи для выставления счета созданы правильно (я вижу их в базе данных, если добавляю точку останова после метода создания записи для выставления счета), но они не извлекаются из базы данных. В результате создается неправильный ежемесячный счет.

Если я снова запускаю код (без очистки базы данных), создаются новые платежные записи, и на шаге 3 извлекаются записи, созданные в первом запуске (но не во втором запуске). Это очень сбивает с толку.

Мой код выглядит следующим образом:

for (User user : usersWithActivities) {

            createBillingEntriesForUser(user.getId());

            userBillingEntries = getLastMonthsBillingEntriesForUser(user.getId());

            createXMLBillForUser(user.getId(), userBillingEntries);
    }

Вызванные методы выглядят следующим образом:

@Transactional
    public void createBillingEntriesForUser(Long id) {

        UserManager userManager = ManagerFactory.getUserManager();
        User user = userManager.getUser(id);
        List<AccountEvent> events = getLastMonthsAccountEventsForUser(id);
        BillingEntry entry = new BillingEntry();

        if (null != events) {

            for (AccountEvent event : events) {

                if (event.getEventType().equals(EventType.ENABLE)) {
                    Calendar cal = Calendar.getInstance();

                    Date eventDate = event.getTimestamp();
                    cal.setTime(eventDate);

                    double startDate = cal.get(Calendar.DATE);
                    double numOfDaysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
                    double numberOfDaysInUse = numOfDaysInMonth - startDate;

                    double fractionToCharge = numberOfDaysInUse/numOfDaysInMonth;

                    BigDecimal amount = BigDecimal.valueOf(fractionToCharge * Prices.MONTHLY_COST);
                    amount.scale();
                    entry.setAmount(amount);
                    entry.setUser(user);
                    entry.setTimestamp(eventDate);

                    userManager.saveOrUpdate(entry);
                }


            }

        }

    }


@Transactional
    public Collection<BillingEntry> getLastMonthsBillingEntriesForUser(Long id) {

        if (log.isDebugEnabled())
            log.debug("Getting all the billing entries for last month for user with ID " + id);

        //String queryString = "select billingEntry from BillingEntry as billingEntry where billingEntry>=:firstOfLastMonth and billingEntry.timestamp<:firstOfCurrentMonth and billingEntry.user=:user";
        String queryString = "select be from BillingEntry as be join be.user as user where user.id=:id and be.timestamp>=:firstOfLastMonth and be.timestamp<:firstOfCurrentMonth";

        //This parameter will be the start of the last month ie. start of billing cycle
        SearchParameter firstOfLastMonth = new SearchParameter();
        firstOfLastMonth.setTemporalType(TemporalType.DATE);

        //this parameter holds the start of the CURRENT month - ie. end of billing cycle
        SearchParameter firstOfCurrentMonth = new SearchParameter();
        firstOfCurrentMonth.setTemporalType(TemporalType.DATE);

        Query query = super.entityManager.createQuery(queryString);

        query.setParameter("firstOfCurrentMonth", getFirstOfCurrentMonth());        
        query.setParameter("firstOfLastMonth", getFirstOfLastMonth());
        query.setParameter("id", id);

        List<BillingEntry> entries = query.getResultList();

        return entries;
    }

public MonthlyBill createXMLBillForUser(Long id, Collection<BillingEntry> billingEntries) {

        BillingHistoryManager manager = ManagerFactory.getBillingHistoryManager();
        UserManager userManager = ManagerFactory.getUserManager();

        MonthlyBill mb = new MonthlyBill();
        User user  = userManager.getUser(id);

        mb.setUser(user);
        mb.setTimestamp(new Date());

        Set<BillingEntry> entries = new HashSet<BillingEntry>();
        entries.addAll(billingEntries);

        String xml = createXmlForMonthlyBill(user, entries);
        mb.setXmlBill(xml);
        mb.setBillingEntries(entries);
        MonthlyBill bill = (MonthlyBill) manager.saveOrUpdate(mb);
        return bill;

    }

Помощь с этим вопросом будет принята с благодарностью, так как она уже несколько недель мучает меня!

Заранее спасибо, Gearoid.

Ответы [ 4 ]

0 голосов
/ 19 марта 2010

Переместите объявление BillingEntry entry = new BillingEntry(); в цикл for. Этот код выглядит так, как будто он обновляет одну запись снова и снова.

Я предполагаю здесь, но то, что вы написали, противоречит тому, что я знаю о стойкости Java и гибернации.

Вы уверены, что эти записи сохраняются правильно? На мой взгляд, происходит то, что создается новый BillingEntry, который затем сохраняется. В этот момент следующая итерация цикла просто меняет значения записи и вызывает слияние. Похоже, что вы ничего не делаете для создания нового BillingEntry после первого раза, поэтому новые идентификаторы не генерируются, поэтому вы не сможете получить их позже.

При этом я не уверен, что выбор времени сброса здесь также не является причиной, так что я буду ждать, затаив дыхание, чтобы понизить голос.

0 голосов
/ 19 марта 2010

Вызов session.flush () AND session.close () до вызова getLastMonthsBillingEntriesForUser.

0 голосов
/ 19 марта 2010

Пожалуйста, исправьте мои предположения, если они не верны ...

Насколько я могу судить, отношение между входом и пользователем много к одному.

Так почему ваш запрос выполняет соединение типа "один ко многим"? Скорее сделайте запрос:

select be from BillingEntry as be where be.user=:user and be.timestamp >= :firstOfLastMonth and be.timestamp < :firstOfCurrentMonth

А затем передайте объект User, а не идентификатор пользователя. Этот запрос будет немного легче, так как ему не придется извлекать детали для пользователя. т.е. не нужно делать выбор для пользователя.

К сожалению, это, вероятно, не является причиной вашей проблемы, но, тем не менее, ее стоит исправить.

0 голосов
/ 19 марта 2010

Ваш лучший метод также транзакционный? Если да, большую часть времени я сталкивался с подобной проблемой, это был сброс, который не был сделан в нужное время гибернацией.

Попробуйте добавить вызов session.flush () в начале метода getLastMonthsBillingEntriesForUser, посмотрите, решит ли он вашу проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...