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