Генератор пользовательских первичных ключей в JPA
Я использую org.hibernate.id.IdentifierGenerator
создать столбец первичного ключа следующим образом. В следующем примере, в настоящее время он просто увеличивает ключ типа INT(11)
(MySQL) последовательно, т.е. auto_increment
в MySQL, но затем его можно использовать для генерации значений любого пользовательского шаблона, например E0001, E0002, E0003 ... E0010 ... E0100 ... E1000 ... E12345 ...
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;
public final class TestIdGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
try {
Connection connection = session.connection();
PreparedStatement ps = connection.prepareStatement("SELECT MAX(id) AS id FROM test");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
int id = rs.getInt("id");
return id <= 0 ? 1 : id + 1;
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
Лицо Test
который использует вышеупомянутый генератор идентификаторов:
public class Test implements Serializable {
@Id
@GenericGenerator(name = "test_sequence", strategy = "com.example.TestIdGenerator")
@GeneratedValue(generator = "test_sequence")
@Basic(optional = false)
@Column(name = "id", nullable = false)
private Integer id;
//...
}
Это, однако, особенность Hibernate. Есть ли способ сделать его независимым от провайдера, т.е. есть ли такая функциональность в JPA (2.1)?
Я использую Hibernate 5.1.0 final с JPA 2.1.
Приводит ли этот подход к некоторым проблемам параллелизма при сохранении, объединении и удалении сущности и требует какой-то блокировки?
1 ответ
Вы не можете сделать это с аннотацией, как вы бы надеялись. Вы можете использовать @PrePersist
крюк.
@PrePersist
public void ensureId() {
id = ...
}
Тем не менее, вы не сможете получить доступ к БД из @PrePersist
метод... спецификация JPA 2 (JSR 317) гласит следующее:
В общем случае метод жизненного цикла переносимого приложения не должен вызывать операции EntityManager или Query, обращаться к другим экземплярам сущности или изменять отношения в одном и том же контексте постоянства.
Что касается реализации, Hibernate запрещает это явно:
Метод обратного вызова не должен вызывать методы EntityManager или Query!
Моя рекомендация: используйте последовательность и выделите отдельный столбец, если вам нужно указать тип. Кроме того, вы всегда можете изменить SELECT
чтобы вернуть шаблон по вашему выбору:
select 'E' || id as id from schema.table;
Или вы можете использовать @PostLoad/@PostPersist для возврата временного поля "formattedId":
@PostLoad
@PostPersist
private void setFormattedId() {
this.formattedId = "E" + id;
}