2015-04-27 5 views
3

Предположим, что я хочу смоделировать отслеживатель проблем, а моя модель состоит из двух объектов: репозитория и проблемы. Репозиторий может содержать несколько проблем. Таким образом, вы получите примерно следующие классы JPA:JPA auto increment для составного ключа

@Entity 
@Table(name="repository") 
public class Repository { 

    @Id 
    @GeneratedValue 
    private Integer id; 

    private String name; 

    // Getters and setters 
} 

А для класса выпуска:

@Entity 
@Table(name="issue") 
@IdClass(Issue.IssuePk) 
public class Issue { 

    static class IssuePk implements Serializable { 

     private Repository repository; 
     private Integer issueNumber; 
     // Getters, setters, equals and hashcode 

    } 

    @Id 
    @ManyToOne 
    private Repository repository; 

    @Id 
    private Integer issueNumber; 

    // Getters and setters 
} 

Теперь я хочу номера проблем, которые будут созданы в автоматическом приращений, как способ, но потом местные к репозиторий. Я вижу различные варианты, но я не уверен, что лучше.

Расчет Id вручную при инстанцировании вопроса

Не используя любую автоматическую генерацию, просто установив значение перед сохраняющимися. Итак: запрашивайте проблемы для репозитория, вычисляйте максимальный номер проблемы (если есть), увеличивайте, устанавливайте и сохраняйте. Это кажется немного склонным к ошибкам, потому что, если вы будете создавать экземпляры из других сегментов кода, вам нужно помнить об этом.

Issue issue = new Issue(); 
issue.setRepository(repository); 
// For example, assuming this is right for the context now: 
issue.setIssueNumber(repository.getIssues().size() + 1); 

(Очевидно, что это один может, быть переработан для дедупликации это поколение номер выпуска, но это не мешает Проблемы с null issueNumber быть persisted на EntityManger)

JPA Lifecycle События

Использование JPA Lifecycle События, подключаясь к @PrePersist и делая то же самое. Это имеет то преимущество, что это называется автоматически и не дублируется над базой кода.

// On the issue entity 
@PrePersist 
void setIssueNumberOnPersist() { 
    if(getIssueNumber() == null) { 
      setIssueNumber(getRepository().getIssues().size() + 1); 
     } 
} 

Однако, кажется, противоречит одному из ограничений JPA:

Чтобы избежать конфликтов с оригинальной работой с базой данных, которая запускает событие сущности жизненного цикла (который все еще находится в процессе разработки) методы обратного вызова не должен вызовите методы EntityManager или Query и не должны обращаться к каким-либо другим объектам объекта.

Использование триггеров базы данных

Установите значение issueId через триггер на столе вопроса, и пусть JPA обновлять это значение после вставки. Суть этого подхода заключается в том, что этот триггер должен быть исправлен при переключении баз данных.

Я на самом деле не в письменном виде триггеров, но я думаю, что было бы грубо:

before insert on issue 
    select max(issue_id) as val from issue where repository = issue.repository 
    issue_id = val + 1 
end 

Или используя кэшированные значения:

before insert on issue 
    select next_issue_id as val from repository where id = issue.repository_id 
    issue_id = val 
    update repository set next_issue_id = val + 1 where id = issue.repository_id 
end 

Использование Hibernate IdentifierGenerator

Это кажется, что IdentifierGenerator также можно будет выполнить эту работу. Однако для этого требуется взаимодействие с БД через запросы, и я думаю, что это нарушит совместимость с различными базами данных и изменениями схемы.

public class IssueIdGenerator implements IdentifierGenerator { 

    public Serializable generate(SessionImplementor session, Object object) 
      throws HibernateException { 

     Connection connection = session.connection(); 
     try { 

      PreparedStatement ps = connection 
        .prepareStatement("... query ..."); 
      // Calculate the next issueId 
      return issueId; 
     } catch (SQLException e) { 
      log.error(e); 
      throw new HibernateException(
        "Unable to generate Stock Code Sequence"); 
     } 
     return null; 
    } 
} 

Опцион что мне не хватает

Если я пропускаю вариант, я был бы рад услышать!

Что является лучшим вариантом?

ответ

1

Другим вариантом является кэширование количества проблем, связанных с репозиторием.

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

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

В соответствии с различными вопросами stackoverflow и сообщениями на форуме это похоже на известную проблему, и Hibernate советует разработчикам создавать логику в своем проекте, а не в зависимости от @GeneratedValue Hibernate.

Смежные вопросы