Предположим, что я хочу смоделировать отслеживатель проблем, а моя модель состоит из двух объектов: репозитория и проблемы. Репозиторий может содержать несколько проблем. Таким образом, вы получите примерно следующие классы 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;
}
}
Опцион что мне не хватает
Если я пропускаю вариант, я был бы рад услышать!
Что является лучшим вариантом?