首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >创建具有唯一字符串和顺序对应数字的复合主键

创建具有唯一字符串和顺序对应数字的复合主键
EN

Stack Overflow用户
提问于 2018-02-11 22:43:55
回答 1查看 2.8K关注 0票数 1

我刚开始使用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-1TICK-2等等开始。而不是像PROJ-1PROJ-2PROJ-3TICK-4TICK-5这样的条目。

我不知道如何用hibernate来完成这个任务。

到目前为止我掌握的代码是。

实体

更新2使生成的TicketIdentifier成为唯一的。

代码语言:javascript
复制
@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使项目非唯一,并从票务端执行。

代码语言:javascript
复制
@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

代码语言:javascript
复制
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组合中这样做的建议都将不胜感激。提前谢谢你!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-12 04:39:14

实现这一目标的一种方法是为主键使用自定义生成策略。(请注意,@GeneratedValue只能与@Id注释一起使用)

要实现自定义生成策略,需要创建一个实现IdentifierGenerator接口或扩展现有生成器之一(例如,SequenceStyleGenerator )的类。然后可以使用@GenericGenerator注释来定义它:

代码语言:javascript
复制
@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实体:

代码语言:javascript
复制
@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-1TICK-1。因此,我已经将列类型更改为VARCHAR

最后一步是实现CustomGenerator。由于您使用的是MySQL,而且它没有序列,所以您必须模仿它们。

代码语言:javascript
复制
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会话来管理它:

代码语言:javascript
复制
@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在不存在的情况下创建新表。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48737287

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档