我刚开始使用Hibernate,并且很难找到一种方法来创建一个复合主键,它同时使用一个唯一的字符串--自动递增(根据唯一字符串的顺序) long。我已经找到了一些资源这里和这里,但仍然有麻烦。
I使用Java8、Hibernate 5、SpringBoot1.5以及Spring 5和MySQL连接器6,其中包含MySQL 5.7
问题
我试图为映射到票务表的实体创建一个主键。我希望主键是类似PROJ-1的东西,我在很多地方都见过,而且我喜欢这个会议。
理想情况下,我希望有两个部分的PK。我想要一个像PROJ这样的唯一的项目代码,以及一个与该项目代码一起递增的票证号,每个票证应该是顺序的:PROJ-1 PROJ-2 PROJ-3,但是如果我制作了另一个像TICK这样的项目代码,那么它应该从一个:TICK-1,TICK-2等等开始。而不是像PROJ-1、PROJ-2、PROJ-3、TICK-4和TICK-5这样的条目。
我不知道如何用hibernate来完成这个任务。
到目前为止我掌握的代码是。
实体
更新2使生成的TicketIdentifier成为唯一的。
@Entity
@Table(name = "support_ticket")
public class SupportTicket implements Serializable {
private TicketIdentifier id;
... other irrelevant properties
@EmbeddedId
@AttributeOverride(name = "id", column = @Column(unique = true, nullable = false))
public TicketIdentifier getId() {
return id;
}
public void setId(TicketIdentifier id) {
this.id = id;
}
... other irrelevant getters and setters
}可嵌入类
UPDATE根据注释将票证编号更改为非唯一值。
更新2使项目非唯一,并从票务端执行。
@Embeddable
public class TicketIdentifier implements Serializable {
String projectId;
Long ticketNum;
public TicketIdentifier() {}
public TicketIdentifier(String projectId) {
this.projectId = projectId;
}
@Column(name = "project", nullable = false, length = 10)
public String getProjectId() {
return projectId;
}
public void setProjectId(String projectId) {
this.projectId = projectId;
}
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "ticket", nullable = false)
public Long getTicketNum() {
return ticketNum;
}
public void setTicketNum(Long ticketNum) {
this.ticketNum = ticketNum;
}
... implementing of hashcode and equals
}表DDL
CREATE TABLE support_ticket
(
project VARCHAR(10) NOT NULL,
ticket BIGINT NOT NULL,
... irrelevant stuff
PRIMARY KEY (project, ticket),
CONSTRAINT UK_6qwbkx66syjgp0jcxn16vkqkd
)但是正如你所看到的,这张票只是一个bigint,而不是看起来更像bigint auto_increment,不知道我在这里做了什么。这非常接近我想要的。除了票证应该自动递增(如“问题”部分中的详细内容)外,我不知道如何(或这是否是正确的地方)在项目和票证列组合之间划出一个破折号。
另外,在下面的屏幕截图中,我也不能做两件事:自动增加ticket的值,并重用相同的项目代码。我可以在DDL中看到后者是因为项目必须是唯一的,但我希望它只是一个独特的组合。

更新
我能够得到组合唯一的(我认为),但仍然无法自动增加的ticket值的方式,我想。
任何关于如何在Java和/或Hibernate组合中这样做的建议都将不胜感激。提前谢谢你!
发布于 2018-02-12 04:39:14
实现这一目标的一种方法是为主键使用自定义生成策略。(请注意,@GeneratedValue只能与@Id注释一起使用)
要实现自定义生成策略,需要创建一个实现IdentifierGenerator接口或扩展现有生成器之一(例如,SequenceStyleGenerator )的类。然后可以使用@GenericGenerator注释来定义它:
@Id
@Column(name = "ticket", nullable = false, insertable = false, updatable = false)
@GenericGenerator(
name = "custom-sequence",
strategy = "your.package.CustomGenerator"
)
@GeneratedValue(generator = "custom-sequence")
public String getTicketNum() {
return ticketNum;
}为了通过示例演示,我以以下方式修改了您的SupportTicket实体:
@Entity
@Table(name = "support_ticket")
public class SupportTicket implements Serializable {
private String projectId;
private String ticketNum;
public SupportTicket() {
}
public SupportTicket(String projectId) {
this.projectId = projectId;
}
@Column(name = "project", unique = true, nullable = false, length = 10)
public String getProjectId() {
return projectId;
}
public void setProjectId(String projectId) {
this.projectId = projectId;
}
@Id
@Column(name = "ticket", nullable = false, insertable = false, updatable = false)
@GenericGenerator(
name = "custom-sequence",
strategy = "your.package.CustomGenerator"
)
@GeneratedValue(generator = "custom-sequence")
public String getTicketNum() {
return ticketNum;
}
public void setTicketNum(String ticketNum) {
this.ticketNum = ticketNum;
}
}ticket列现在是一个主键,它将保存由CustomGenerator生成的值,如PROJ-1和TICK-1。因此,我已经将列类型更改为VARCHAR。
最后一步是实现CustomGenerator。由于您使用的是MySQL,而且它没有序列,所以您必须模仿它们。
public class CustomGenerator implements IdentifierGenerator {
@Override
public synchronized Serializable generate(SessionImplementor session, Object obj) {
if (obj instanceof SupportTicket) {
String sequenceName = ((SupportTicket) obj).getProjectId();
Serializable result = null;
try {
Connection c = session.connection();
Statement s = c.createStatement();
s.execute("CREATE TABLE IF NOT EXISTS sequences\n" +
" (\n" +
" name VARCHAR(70) NOT NULL UNIQUE,\n" +
" next INT NOT NULL\n" +
" );");
s.execute("INSERT INTO sequences (name, next)\n" +
"VALUES ('" + sequenceName + "', @current := 1)\n" +
"ON DUPLICATE KEY UPDATE \n" +
"next = @current := next + 1");
ResultSet resultSet = s.executeQuery("SELECT @current");
if (resultSet.next()) {
int nextValue = resultSet.getInt(1);
result = sequenceName + "-" + nextValue;
}
} catch (SQLException e) {
e.printStackTrace();
}
return result;
} else {
return null;
}
}
}这个自定义生成器将创建一个sequences表,其中每一行对应于特定的projectId,并使用它来增加ticketNum字段。
更新:也可以为sequences表创建一个实体并使用hibernate会话来管理它:
@Override
public synchronized Serializable generate(SessionImplementor session, Object obj) {
if (obj instanceof SupportTicket) {
Session s = (Session) session;
String sequenceName = ((SupportTicket) obj).getProjectId();
Sequences sequences = (Sequences) s.get(Sequences.class, sequenceName);
long next = 1;
if (sequences == null) {
sequences = new Sequences(sequenceName, next);
} else {
next = sequences.getNext() + 1;
sequences.setNext(next);
}
s.saveOrUpdate(sequences);
return sequenceName + "-" + next;
} else {
return null;
}
}不要忘记将spring.jpa.properties.hibernate.hbm2ddl.auto=update添加到属性中,以强制hibernate在不存在的情况下创建新表。
https://stackoverflow.com/questions/48737287
复制相似问题