Auto increment JPA for composite key

Suppose I want to simulate a bug tracker and my model consists of two objects: repository and problem. The repository can contain several problems. So you end up with roughly the following JPA classes:

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

    @Id
    @GeneratedValue 
    private Integer id;

    private String name;

    // Getters and setters
}

      

And for the Issue class:

@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
}

      

Now I want the issue numbers to be generated automatically as shown in the picture, but then locally in the repository. I see various options, but I'm not sure which is better.

Calculate ID manually when instantiating an issue

Don't use auto-creation, just setting the value before saving. So: request issues for the repository, calculate the maximum issue number (if any), increment, install and save. This seems a little prone to errors because if you will be instantiating from other code segments you need to keep this in mind.

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

      

(Obviously, it is possible to reorganize the deduplication of this issue the issue number, but it does not prevent problems with null

issueNumber persisted

on EntityManger

)

JPA Lifecycle Events

Using JPA lifecycle events, connecting to @PrePersist

and doing there. This has the advantage of being called automatically and not duplicated over the code base.

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

      

However, it seems to conflict with one of the JPA restrictions:

To avoid conflicts with the original database operation that triggers the object's lifecycle event (which is still running), callback methods should not call the EntityManager or Query methods and should not access any other object objects.

Using database triggers

Set the issue value with a trigger on the issue table and let JPA update its value after insertion. The gist of this approach is that this trigger must be fixed when switching databases.

I'm not currently going to write triggers, but I think it would be something like this:

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

      

Or using a cached value:

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

      

Using Hibernate IdentifierGenerator

It seems that IdentifierGenerator

it will also be possible to do this job. However, this requires interacting with the DB via queries, and I think this would break compatibility with various databases and schema changes.

 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;
     }
 }

      

The option I am missing

If I'm missing an option, I'd love to hear!

What's the best option?

+3


source to share


1 answer


Another option would be to cache the number of repository-related issues.

So you create a field issueCount

that will be initialized in the constructor by fetching an amount from the database and then incrementing accordingly each time a problem arises.



Keep in mind that this needs to be in sync as you don't want you to have 2 problems with the same ID.

According to various stackoverflow questions and forum posts, this seems to be a known issue and Hibernate advises developers to create logic in their project, not depending on @GeneratedValue

Hibernate.

+1


source







All Articles