Issue
I am new to Spring and Hibernate and ran into a problem that I cannot solve for a few more days.
In my project, I use a Many-To-Many relationship between the Server and User entities. A user can exist on multiple servers.
My idea is: Servers <-> Server_Users <-> Users
Code for creating tables:
CREATE TABLE public.servers (
id bigint NOT NULL,
s_snowflake bigint NOT NULL,
name character varying(64) NOT NULL
);
CREATE TABLE public.users (
id bigint NOT NULL,
u_snowflake bigint NOT NULL,
name character varying(64) NOT NULL
);
CREATE TABLE public.server_users (
id bigint NOT NULL,
server_id bigint NOT NULL,
user_id bigint NOT NULL
);
This is how my classes look like:
@Entity
@Table(name = "servers")
public class Server {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "s_snowflake", unique = true)
private long snowflake;
private String name;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(
name = "server_users",
joinColumns = @JoinColumn(name = "server_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private Set<User> users = new HashSet<>();
}
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "u_snowflake", unique = true)
private long snowflake;
private String name;
@ManyToMany(mappedBy = "users", targetEntity = Server.class, fetch = FetchType.EAGER)
private Set<Server> servers = new HashSet<>();
}
This works well when I initialize the data. I create a Server object and populate it with users and everything is saved correctly.
But I get problems when I try to save a server that contains users that are already in the Users table (users shouldn't get deleted if I cascade delete the server from the database).
Is there any way to configure Hibernate so that it adds new Users if necessary, but if the User exists, "create relationship" it to new servers using the Server_Users table?
I tried putting Cascade.SAVE_UPDATE and another ways, but it gave no results.
Addiction:
This is DAO class methods that I use to save and update:
public abstract class AbstractDAOImpl<T> implements AbstractDAO<T> {
public void save(T entity) {
Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
Transaction trx = session.beginTransaction();
session.save(entity);
trx.commit();
session.close();
}
public void update(T entity) {
Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
Transaction trx = session.beginTransaction();
session.update(entity);
trx.commit();
session.close();
}
}
Server adding logic. First of all, I save it to the database, then fill the Set with users and execute update()
This works if the table with users is empty, all necessary records and connections are created in the Server_Users and Users tables:
public static void addServer(Guild guild) {
Server server = new Server(Long.parseLong(guild.getId()), guild.getName());
serverDAO.save(server);
for (Member m : guild.getMembers()) {
User user = new User(Long.parseLong(m.getId()), m.getUser().getName());
user.addServer(server);
server.addUser(user);
}
serverDAO.update(server);
}
Solution
I found a solution
@Cascade({
org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.MERGE,
org.hibernate.annotations.CascadeType.PERSIST
})
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "server_users",
joinColumns = @JoinColumn(name = "server_sn"),
inverseJoinColumns = @JoinColumn(name = "user_sn")
)
private Set<User> users = new HashSet<>();
I added CascadeTypes SAVE_UPDATE, MERGE and PERSIST to Server and User code
In addition, I redesigned the architecture of the base, making snowflakes as Primary Keys, because values are always unique
Answered By - KubePony
Answer Checked By - Willingham (JavaFixing Volunteer)