Updating and deleting from DB using JpaRepository - java

I have two entities which are connected with ManyToMany relation:
User.java
#Id
#Column(name = "user_id", updatable = false, nullable = false, unique = true)
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#Column(name = "name")
private String name;
#Column(name = "product")
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinTable(name = "products_users",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "product_id")})
private Set<Product> products;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd#HH:mm:ss")
#Column(name = "created_on")
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Date createdOn;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd#HH:mm:ss")
#Column(name = "modified_on")
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Date modifiedOn;
// construuctors, getter, setter
}
Product .java
#Id
#Column(name = "product_id", updatable = false, nullable = false, unique = true)
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#Column(name = "name")
private String name;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd#HH:mm:ss")
#Column(name = "created_on")
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Date createdOn;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd#HH:mm:ss")
#Column(name = "modified_on")
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Date modifiedOn;
//getters, setters, contructors
}
I want to delete data from DB by users UUID. How i write query like this
#Transactional
#Modifying
#Query(value = "delete from users where user_id = ?1", nativeQuery = true)
void deleteByUUID(UUID uuid);
but it deletes the only row in the users table. I want all the data about this user and his products to be deleted.
And also I don't know how to perform an update of user and it's products correctly.

You have all the right set up regarding the cascading:
The problem is that you are triggering a native query. This bypasses all the JPA configuration and cascading altogether. Even if you used a JPQL delete without native, this would still omit all the cascading and JPA config.
Somewhere in your code, you need to fetch the User, using findOne for example. After that use the delete method. Only then the cascading will work.

Based on Maciej Kowalski answer
To find by UUID on repository:
Optional<User> findByUUID(UUID uuid);
On service to delete:
Optional<User> optionalUser = repository.findByUUID(uuid);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
repository.delete(user);
}
// here on else you can throw an Exception
On service to update:
Optional<User> optionalUser = repository.findByUUID(uuid);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
// Make changes here for example user.setName(otherName)
repository.save(user);
}
// here on else you can throw an Exception

Related

Hibernate Envers: Query deleted Entity by value of foreign key column

Hi hibernate (envers) experts!
I currently have two/three entities
Automations which represent an automated process for one answer of a question (of a survey)
Answers answer of a user for a question (of a survey)
BaseEntity which is extended by Automations and Answers an has basic attributes like modify timestamps and so on.
The Automations table is audited by hibernate envers.
I want to fetch all deleted Automations for one specific Answer. The generated and executed query from hibernate does include my condition for the "deleted" revtype of envers but not my condition for the answer.
See the function listHistoryByAnswer
#MappedSuperclass
public class BaseEntity extends PanacheEntityBase {
#Id
#Column(name = "UUID", updatable = false, nullable = false, columnDefinition = "uuid")
private UUID _uuid;
#Column(name = "CREATED_AT", nullable = false, updatable = false, columnDefinition = "TIMESTAMP without time zone")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "CREATED_BY", nullable = false, updatable = false)
private String createdBy;
#Column(name = "UPDATED_AT", columnDefinition = "TIMESTAMP without time zone")
#UpdateTimestamp
private Timestamp updatedAt;
#Column(name = "UPDATED_BY")
private String updatedBy;
}
#Entity
#Table(name = "AUTOMATION", uniqueConstraints = #UniqueConstraint(columnNames = {"ANSWER_UUID"}))
#Audited
public class Automation extends BaseEntity {
#Basic
#Column(name = "DESCRIPTION")
private String description;
#JsonBackReference("automation-answer")
#ManyToOne
#JoinColumn(name = "ANSWER_UUID", nullable = false)
private Answer answer;
}
#Entity
#Table(name = "ANSWER")
#Audited(targetAuditMode = NOT_AUDITED)
public class Answer extends BaseEntity {
#JsonManagedReference("automation-answer")
#OneToMany(mappedBy = "answer", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Automation> automations = new ArrayList<>();
#Column(name = "DATA", columnDefinition = "jsonb")
private JsonNode data;
}
/**
* List old (deleted) questions based on the answer
* #param answerUuid unique id of the answer
* #return list with deleted questions
*/
public List<Automation> listHistoryByAnswer(final UUID answerUuid){
List resultList = AuditReaderFactory.get(getEntityManager())
.createQuery()
.forRevisionsOfEntity(Automation.class, true)
.add(AuditEntity.revisionType().eq(RevisionType.DEL))
.add(AuditEntity.property("answer_uuid").eq(answerUuid))
.getResultList();
return resultList;
}
Generated SQL
select automation0_.UUID as uuid1_1_0_,
automation0_.REV as rev2_1_0_,
defaultrev1_.REV as rev1_15_1_,
automation0_.REVTYPE as revtype3_1_0_,
automation0_.ANSWER_UUID as answer_u9_1_0_,
defaultrev1_.REVTSTMP as revtstmp2_15_1_
from A_AUTOMATION_HIS automation0_
cross join
REVINFO defaultrev1_
where automation0_.REVTYPE = 2 -- DELETED entities
and automation0_.REV = defaultrev1_.REV
order by automation0_.REV asc;

How to get rid of cyclic redundancy while having #ManyToMany relation JPA springboot

I am a newbie to the Spring boot (but worked in Laravel). I am facing a problem of cyclic redundancy in #ManyToMany relation. Let's go through the scenario -
What response I ma getting (fetching user's list which has many to many relationships with roles) -
Following is the ER-diagram of associated tables to manage many to many relationship between users and roles table.
User entity class has following code -
#Entity
#Where(clause = "deleted_at IS NULL")
#SQLDelete(sql = "UPDATE users SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?", check = ResultCheckStyle.COUNT)
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "users")
#JsonIgnoreProperties(
value = {"createdAt", "updatedAt", "deletedAt"}
)
public class User {
#Id
#Column(name = "id", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name", nullable = false)
#NotBlank(message = "Name field can not be empty")
private String name;
.....
.....
.....
#ManyToMany(targetEntity = Role.class, fetch = FetchType.EAGER)
#JoinTable(name = "user_roles",joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
}
And Role entity is as follows -
#Entity
#Table(name = "roles")
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#SQLDelete(sql = "UPDATE roles SET deleted_at = CURRENT_TIMESTAMP WHERE id = ?", check = ResultCheckStyle.COUNT)
#Where(clause = "deleted_at IS NULL")
#JsonIgnoreProperties(
value = {"createdAt", "updatedAt", "deletedAt"}
)
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "title")
#NotBlank(message = "Title field must be not null")
private String title;
......
......
......
#OneToMany(targetEntity = User.class, fetch = FetchType.EAGER)
#JoinTable(name = "user_roles",joinColumns = #JoinColumn(name = "role_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
private List<User> users;
}
How to solve this problem? What I am doing wrong here?
Since you are fetching the list directly. You will have to mention the annotation #JsonIgnore everywhere you have mapping specified. By everywhere I don't mean literally everywhere. Just use the annotation and see how it works.
Edit -> Just do it in roles table where you have mapped it to the user table. It will then skip the user mapping while fetching the data.
#JsonIgnore
private List<User> users;
You could annotate users within Role with #JsonBackReference.
Easiest would probably be to annotate the List<T>'s with a #JsonIgnoreProperties annotation to break the cyclic dependencies.
#JsonIgnoreProperties("users")
private List<Role> roles;
#JsonIgnoreProperties("roles")
private List<User> users;

How to use UUID Generator in Spring DATA JPA?

I want to join two models, both are using org.hibernate.id.UUIDGenerator for primary key.
But on startup, I get the following error:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error
executing DDL "alter table user_role add constraint
FK5scdquo6f12cpstqai86x4biw foreign key (roles_role_id) references
role (role_id)" via JDBC Statement
Do you know, what I'm doing wrong?
My code:
User Model:
#Entity
#Table
public class User implements Serializable {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "user_id", columnDefinition = "VARCHAR(255)")
private String userId;
#Column(name = "name")
private String name;
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "userId"), inverseJoinColumns = #JoinColumn(name = "roleId"))
#ManyToMany
private List<Role> roles;
public User(){
this.roles = new ArrayList<>();
}
// Getter & Setter
}
Role Model:
#Entity
#Table
public class Role implements Serializable {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "role_id", columnDefinition = "VARCHAR(255)")
private String roleId;
#Column(name = "role_name")
private String name;
#Column(name = "description")
private String description;
#ManyToMany(mappedBy = "roles")
private List<User> users;
public Role(){
this.users = new ArrayList<>();
}
// Getter & Setter
}
User DAO:
public interface UserDAO extends JpaRepository<User, String > {
}
Role DAO:
public interface RoleDAO extends JpaRepository<Role, String > {
}
Your join column should have a name similar to the column name and not the model variable name. In your case you should use
joinColumns = #JoinColumn(name = "user_id")
and
inverseJoinColumns = #JoinColumn(name = "role_id"))
NOT
joinColumns = #JoinColumn(name = "userId")
and
inverseJoinColumns = #JoinColumn(name = "roleId"))
Also do this for all join columns

The abstract schema type 'User_Book' is unknown

I have a database with several entities, in particular Book and User. Between them there exists a ManyToMany relationship like this:
Book:
#Entity
#Table(name = "Books")
public class Book implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "bookId", nullable = false, unique = true)
private Long id;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "price", nullable = false)
private int price;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "bookId"),
inverseJoinColumns = #JoinColumn(name = "userId"))
private Set<UserAccount> users;
User:
#Entity
#Table(name = "UserAccounts")
public class UserAccount implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "userId", nullable = false, unique = true)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "userId"),
inverseJoinColumns = #JoinColumn(name = "bookId"))
Set<Book> purchasedBooks;
Everything works fine, the table User_Book is indeed created in the database. The problem seems to be related to the access of this Table.
For example,
Query query = entityManager.createQuery("SELECT u FROM User_Book u");
keeps telling me the following:
The abstract schema type 'User_Book' is unknown
So, shall I create from scratch the User_Book entity? Will it get automtically populated like now, that is, whenever a user buys a book, will this purchase be recorded in the table?
User_Book is not an entity. Therefore you cannot use createQuery, BUT you can use createNativeQuery to execute a SQL query:
Query query = entityManager.createNativeQuery("SELECT * FROM User_Book");
The result will be List<Object[]>

Hibernate ORA-02292: integrity constraint (ROOT.SYS_C007062) violated - child record found

I following have hibernate entities:
#Entity
#Table(name = "News")
public final class News implements Serializable, IEntity {
private static final long serialVersionUID = 3773281197317274020L;
#Id
#SequenceGenerator(name = "NEWS_SEQ_GEN", sequenceName = "NEWS_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "NEWS_SEQ_GEN")
#Column(name = "NEWS_ID", precision = 0)
private Long newsId; // Primary key
#Column(name = "TITLE")
private String title;
#Column(name = "SHORT_TEXT")
private String shortText;
#Column(name = "FULL_TEXT")
private String fullText;
#Temporal(TemporalType.DATE)
#Column(name = "CREATION_DATE")
private Date creationDate;
#Temporal(TemporalType.DATE)
#Column(name = "MODIFICATION_DATE")
private Date modificationDate;
#OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
#JoinColumn(name = "NEWS_ID", updatable = false, referencedColumnName = "NEWS_ID")
#OrderBy("creationDate ASC")
private List<Comment> commentsList;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "NEWS_TAG", joinColumns = { #JoinColumn(name = "NEWS_ID") }, inverseJoinColumns = { #JoinColumn(name = "TAG_ID") })
private Set<Tag> tagSet;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "NEWS_AUTHOR", joinColumns = { #JoinColumn(name = "NEWS_ID") }, inverseJoinColumns = { #JoinColumn(name = "AUTHOR_ID") })
private Set<Author> author;
And the second:
#SequenceGenerator(name = "COMMENTS_SEQ", sequenceName = "COMMENTS_SEQ")
#Entity
#Table(name = "Comments")
public class Comment implements Serializable, IEntity {
private static final long serialVersionUID = 3431305873409011465L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "COMMENTS_SEQ")
#Column(name = "COMMENT_ID", precision = 0)
private Long commentId; // Primary key
#Column(name = "NEWS_ID")
private Long newsId;
#NotEmpty
#NotNull
#Column(name = "COMMENT_TEXT")
private String commentText;
#Temporal(TemporalType.DATE)
#Column(name = "CREATION_DATE")
private Date creationDate;
When I'm trying to remove entity News, I get the exception ORA-02292: integrity constraint (ROOT.SYS_C007062) violated - child record found. So, if I remove the property "updatable = false" it tries to set nullable fields into property Comment. What is my mistake? Please, help.
Thanks.
Because your news records have a one to one or one to many relation with comments. You most likely did not specifcy a CACASDE ON DELETE clause while defining your table. in order to delete entity NEWS you have to make sure that all of its related comments records are deleted or are referencing another NEWS record.
basicaly the definition of the ORA 02292 exception.

Categories

Resources