Why Hibernate does delete after update entity - java

Im learning Hibernate and cant solve problem.
Why Hibernate does delete in table "writer_post" after update entity Writer?
Table writer_post have writer_id and post_id entity(Writer, Post) and have annotation #MoreToOne.
What did I do wrong?
Update:
I updating existing Writer. Same in table existing row for Writer.
Example:
Writer: id: 1, first_name: Dmitry, last_name: Polischuk.
Writer_post: writer_id: 1, post_id: 1.
I does update parameter only last_name.
Create tables
CREATE TABLE IF NOT EXISTS Label(
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS Writer(
id SERIAL PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
CREATE TABLE IF NOT EXISTS Post(
id SERIAL PRIMARY KEY,
content VARCHAR(100) NOT NULL,
create_date TIMESTAMP DEFAULT NULL,
updated_date TIMESTAMP DEFAULT NULL,
status VARCHAR(30) NOT NULL
);
CREATE TABLE IF NOT EXISTS Post_Label(
label_id INT NOT NULL,
post_id INT NOT NULL,
FOREIGN KEY (post_id) REFERENCES Post(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (label_id) REFERENCES Label(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS Writer_Post(
post_id INT NOT NULL,
writer_id INT NOT NULL,
FOREIGN KEY (post_id) REFERENCES Post(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (writer_id) REFERENCES Writer(id) ON DELETE CASCADE ON UPDATE CASCADE
);
Entity Writer
#Getter
#Setter
#RequiredArgsConstructor
#Entity
#Table(name = "Writer")
public class Writer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#ManyToOne(targetEntity = Post.class, cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinTable(name = "writer_post",
joinColumns = #JoinColumn(name = "writer_id"),
inverseJoinColumns = #JoinColumn(name = "post_id"))
private List<Post> posts;
Entity Post
#Getter
#Setter
#RequiredArgsConstructor
#Entity
#Table(name = "Post")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "Content")
private String content;
#Column(name = "create_date")
private Date created;
#Column(name = "updated_date")
private Date updated;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private PostStatus status;
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinTable(name = "post_label",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "label_id"))
private List<Label> labels;
Code for update Writer
#Override
public Writer update(Writer writer) {
try(Session session = HibernateUtil.getSessionFactory().openSession()){
session.beginTransaction();
session.saveOrUpdate(writer);
session.getTransaction().commit();
return writer;
}
}
Result stacktrace
Hibernate: update Writer set first_name=?, last_name=? where id=?
Hibernate: delete from writer_post where writer_id=?

Your mapping looks like one post can be mapped to many writers. I guess you meant vice-versa. So, it's worth trying to change this:
#ManyToOne(targetEntity = Post.class, cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinTable(name = "writer_post",
joinColumns = #JoinColumn(name = "writer_id"),
inverseJoinColumns = #JoinColumn(name = "post_id"))
private List<Post> posts;
to this:
#OneToMany(targetEntity = Post.class, cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinTable(name = "writer_post",
joinColumns = #JoinColumn(name = "writer_id"),
inverseJoinColumns = #JoinColumn(name = "post_id"))
private List<Post> posts;

Related

How to delete only the relation in a many to many relationship?

I have the following three tables:
player (id, name)
status (id, status_text)
player_status (player_id, status_id)
The player_status table combines the first two tables with a n-to-n relationship
Table "player":
id
player
agzua76t34gusad
"Anna"
sdahb433tbjsdbv
"Julia"
Table "status":
id
status_text
jjbsdnv8677v6df
"operational"
bulsiu783fdszjh
"violated"
Table "player_status"
record_id
record_status_id
agzua76t34gusad
jjbsdnv8677v6df
sdahb433tbjsdbv
bulsiu783fdszjh
The player can have a status assigned or not.
Now when a player has a status, how can I remove this status, so that the player but also the status stays in the tables but only the relation in the player_status table will be removed.
These are the classes for Player and Status
#Entity
#Table(name = "player")
public class Player {
#Column(nullable = false)
private String id;
#Column(nullable = false)
private String name;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "player_status",
joinColumns = {#JoinColumn(name = "player_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "status_id", referencedColumnName = "id")})
private Set<Status> statusList = new HashSet<>();
}
#Entity
#Table(name = "status")
public class Status {
#Column(nullable = false)
private String id;
#Column(name = "status_text", nullable = false)
private String statusText;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}, mappedBy = "statusList")
#JsonIgnore
private Set<Player> players = new HashSet<>();
}
This is how the relation table is created in *.sql:
create table player_status
(
player_id varchar references player (id) on update cascade on delete cascade,
status_id varchar references status (id) on update cascade
);
How can I delete only an entry from the player_status table? I tried to retrieve a player from the db, changed/removed his status, but this did not update the player.
You just remove the associated Status from the statusList collection, and the row of the association table will be automatically deleted when the EntityManager is flushed.

how do i create entity class for a table having primary key as foreign key?

how do i create entity for area_item_table which have foreign key as primary key
the Item entity:
#Entity
#Table(name = Item.ITEM)
public class Item {
#Id
#Column(name = ITEM_ID)
private long itemId;
#OneToOne
#JoinTable(name = "area_item_table",
joinColumns = {#JoinColumn(name = ITEM_ID, referencedColumnName = ITEM_ID)},
inverseJoinColumns = {
#JoinColumn(name = AREA_ID, referencedColumnName = ID)})
private Area area;
[removed get set and unrelated fields from entity]
}
the Area entity:
#Entity
#Table(name = Area.AREA)
public class Area {
#Id
#Column(name = ID, nullable = false)
private Long id;
[removed get set and unrelated fields from entity]
the table area_item_table :
item_id
area_id
1
121
is there a way to create an Entity for this table without creating new primary key field

Many to many on delete set NULL

I have a many to many relationship between user and group with addtional columns in the join table. It looks like this:
When I delete an user, it should remove all his references from user_to_group(which works) and all the groups created by him should remain and have their created_by field updated to NULL(this doesn't happen, all the entries are deleted).
DDL for schema:
CREATE TABLE user (
user_id int NOT NULL AUTO_INCREMENT,
first_name varchar(100) NOT NULL,
last_name varchar(100) NOT NULL,
username varchar(100) NOT NULL,
email_address varchar(100) UNIQUE NOT NULL,
phone_number varchar(100) NOT NULL,
password varchar(255) NOT NULL,
notification_type varchar(30) NOT NULL DEFAULT "email",
date_created datetime NOT NULL,
is_active bool NOT NULL DEFAULT false,
CONSTRAINT user_pk PRIMARY KEY (user_id)
);
CREATE TABLE `group` (
group_id int NOT NULL AUTO_INCREMENT,
name varchar(100) NULL,
date_created datetime NOT NULL,
is_private bool NOT NULL DEFAULT false,
created_by int NULL,
CONSTRAINT group_pk PRIMARY KEY (group_id),
CONSTRAINT group_user_fk FOREIGN KEY(created_by)
REFERENCES user (user_id) ON DELETE SET NULL
);
CREATE TABLE user_to_group (
user_id int NOT NULL,
group_id int NOT NULL,
user_type_id int NOT NULL,
is_blocked bool NOT NULL DEFAULT false,
CONSTRAINT user_to_group_pk PRIMARY KEY (user_id,group_id),
CONSTRAINT user_to_group_group_fk FOREIGN KEY(group_id)
REFERENCES `group` (group_id),
CONSTRAINT user_to_group_user_type_fk FOREIGN KEY(user_type_id)
REFERENCES user_type (id),
CONSTRAINT user_to_group_user_fk FOREIGN KEY(user_id)
REFERENCES user (user_id)
);
User Entity:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "username",
unique = true)
private String username;
#Column(name = "email_address",
unique = true)
private String emailAddress;
#Column(name = "phone_number")
private String phoneNumber;
#Column(name = "password")
private String password;
#Column(name = "notification_type",
insertable = false)
private String notificationType = "email";
#Column(name = "date_created")
private Date dateCreated;
#Column(name = "is_active",
insertable = false)
private Boolean active = false;
#OneToMany(
mappedBy = "user",
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true
)
private List<UserGroup> groups;
}
Group Entity:
#Entity
#Table(name = "`group`")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "date_created")
private Date dateCreated;
#Column(name = "is_private",
insertable = false)
private Boolean privateG = false;
#OneToOne(fetch = FetchType.LAZY,
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true)
#JoinColumn(name = "created_by")
private User createdBy;
#OneToMany(
mappedBy = "group",
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true
)
private List<UserGroup> users = new ArrayList<>();
}
UserGroup(join table):
#Entity
#Table(name = "user_to_group")
public class UserGroup {
#EmbeddedId
private UserGroupId id;
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#MapsId("userId")
#JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#MapsId("groupId")
#JoinColumn(name = "group_id", insertable = false, updatable = false)
private Group group;
#Column(name = "is_blocked",
insertable = false)
private boolean isBlocked = false;
}
Ignore user_type_id field on the join table. If I delete an user with on the workbench, it works as expected(created_by field updates to NULL). But if i use this:
#Override
#Transactional
public User deleteUser(Long id) {
Optional<User> userToDelete = userRepository.findById(id);
userToDelete.ifPresent(user -> userRepository.delete(user));
return userToDelete.orElseThrow(() -> new UserNotFoundException("User not found"));
}
the entire row in the group table is deleted. What am I doing wrong?
#ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#MapsId("groupId")
#JoinColumn(name = "group_id", insertable = false, updatable = false)
private Group group;
exclude CascadeType.REMOVE and group will be intact.

Persisting not working when ManyToOne used with OneToMany thru relation table

I have 3 entities User, Order, Item with a mapping like this:
#Entity
public class Item {
#Id
#Access(AccessType.PROPERTY)
private long id; // item id predefined
//..getters and setters
}
#Entity
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//...getters and setters
}
#Entity
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false)
#Access(AccessType.PROPERTY)
private Long id;
#ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.DETACH
})// without removing
//#JoinColumn(name = "client_id") - works fine
#JoinTable(name = "client_cr_order_cr_item",
joinColumns = #JoinColumn(name = "order_id"),
inverseJoinColumns = #JoinColumn(name = "client_id"))
private Client client;
#NotEmpty
#OneToMany(fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.DETACH
})// without removing
#JoinTable(name = "client_cr_order_cr_item",
joinColumns = #JoinColumn(name = "order_id"),
inverseJoinColumns = #JoinColumn(name = "item_id"))
private List<Item> items;
//...getters and setters
}
When I persist order it fails with error:
ERROR: null value in column "item_id" violates not-null constraint
it generate query like this:
insert into client_cr_order_cr_item (client_id, order_id) values ()
i.e. it do not fill out segment_id field by some reason I don't know. But I refer to client not thru 3th table but using FK #JoinColumn(name = "client_id") , then it generate correct query:
insert into order_cr_item (order_id, item_id) values ()
Please, could you explain this behavior? Why mapping of client affect items? Is there any hint to make hibernate persists items to 3th table?

Why Hibernate tries to insert new record with existed id?

Why Hibernate tries to insert new record with existing id?
Category table before save (was filled manually with insert)
ID | NAME | IMAGE
1 | 'NAME'| 'IMAGE'
Save code
//Category{id=0, name='NAME', image='IMAGE', parent=null}
getCurrentSession().save(category);
Should be inserted with id 2.
Category Java code
#Entity
#Table(name = "CATEGORY",
indexes = {
#Index(name = "CATEGORY_NAME_INDEX",
columnList = "CATEGORY_NAME")})
public class Category implements NamedModel {
#Id
#Column(name = "CATEGORY_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull
#Size(min = 1, max = 256)
#Column(name = "CATEGORY_NAME", length = 256)
private String name;
#OneToOne(fetch = FetchType.EAGER)
#JoinTable(name = "CATEGORY_RELATIONS",
joinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_CATEGORY_ID",
referencedColumnName = "CATEGORY_ID")},
inverseJoinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_PARENT_ID",
referencedColumnName = "CATEGORY_ID")})
private Category parent;
#OneToMany(cascade = {CascadeType.REMOVE, CascadeType.PERSIST},
fetch = FetchType.LAZY, mappedBy = "parent")
private List<Category> children;
}
SOLUTION
//Category
#GeneratedValue(strategy = GenerationType.IDENTITY)
//CategoryRelations
#Entity
#IdClass(CategoryRelationsPrimaryKey.class)
public static class CategoryRelationsPrimaryKey implements Serializable {
private Long categoryId;
private Long parentId;
instead of long.
This is because you had deleted/added a record from database directly instead from the application i.e. ORM, that's why values in hibernate_sequence is no longer maintained.
Hibernate maintains values in a hibernate_sequence table which would be inserted on creating a new record.
update next_val column value in hibernate_sequence to resolve the problem.
You can use Annotation #GeneratedValue(strategy = GenerationType.IDENTITY) to delegate primary key generation to database.

Categories