Hibernate: Generated value composite key

17 Views Asked by At

I have a problem with automatic ID generation for a composite key.

I would like to point out that I have no possibility of modifying the DB schema and converting the composite key into a simple key.

Below is a simplified example of my case:

  • master entity (in my example User) with an automatically generated key (id)
  • child entity (in my example Post) with a composite key (user_id, post_id) where one of the key columns (user_id) has a foreign key to the User entity.

What I would need is to automatically generate a new id for each post added but the progression should be broken down by user.

User

id username
1 UserA
2 UserB

By inserting 2 new posts for UserA I should find myself in this case:

Posts

user_id post_id
1 1
1 2

By adding a new post for UserB, post_id should be 1 again. What I would like is this

Posts

user_id post_id
1 1
1 2
2 1

This is the code to recreate the case where I manually set the value of post_id. Is it possible to automatically generate post_id by restarting the numbering for each user?

DB schema (PostgreSQL)

CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY
  , username VARCHAR(128)
);

CREATE TABLE posts (
    user_id BIGSERIAL
  , post_id BIGSERIAL
  , title VARCHAR(128)
  , CONSTRAINT PK_posts PRIMARY KEY (user_id, post_id)
  , CONSTRAINT FK_posts FOREIGN KEY (user_id) REFERENCES users (id)
);

User class

@Entity
@Table(name = "users")
public class User {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @Column
    private String username;
    
    @OneToMany(mappedBy="id.user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Post> posts = new HashSet<Post>();
    
    public User(String username) {
        this.username = username;
    }
    
    public void addPost(Post post) {
        this.posts.add(post);
        post.getId().setUser(this);
    }
    
    // Constructors, getter, setter ...
}

PostId class

@Embeddable
public class PostId implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;
    
    @Column(name = "post_id")
    private long postId;
    
    public PostId(long postId2) {
        this.postId = postId2;
    }
    
    // Constructors, getter, setter ...
}

Post class

@Entity
@Table(name = "posts")
public class Post {
    @EmbeddedId
    private PostId id;
    
    @Column
    private String title;
    
    public Post(long postId, String title) {
        this.id = new PostId(postId);
        this.title = title;
    }
    
    // Constructors, getter, setter ...
}

Main class

public class Main {
    private static SessionFactory sessionFactory;
    
    public static SessionFactory getSessionFactory() {
        if(sessionFactory==null)
            sessionFactory = new Configuration().configure().buildSessionFactory();
        return sessionFactory;
    }
    
    public static void main(String[] args) {
        /* User 1 */
        User user1 = new User("UserA");
        user1.addPost(new Post(1, "First post by " + user1.getUsername()));
        user1.addPost(new Post(2, "Second post by "  + user1.getUsername()));
        
        /* User 2 */
        User user2 = new User("UserB");
        user2.addPost(new Post(1, "First post by "  + user2.getUsername()));
        user2.addPost(new Post(2, "Second post by "  + user2.getUsername()));
        
        Session session = getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();
        session.save(user1);
        session.save(user2);
        try {
            transaction.commit();
        } catch (javax.persistence.PersistenceException e) {
            e.printStackTrace();
            transaction.rollback();
        }
        session.close();
    }
}
0

There are 0 best solutions below