Getting old-entity with JPA (Hibernate) - java

I am getting old entity-data with JPA displayed in my JSF-Page, even if I disable the second-level-cache of hibernate.
There are three entities: MessageEntities contain DataEntities and those contain SubDataEntities.
SubDataEntities are extended by two different types.
I temporary delete an instance of SubDataEntity by setting its data-attribute to null and remove the instance from the collection in parent-entity (DataEntity).
After ajax-request, the old data is still displayed in JSF-Page, also if I really execute a successfull delete-operation on DB.
MessageEntity:
#Entity(name = "Message")
#Table(name = "message")
public class MessageEntity {
#Column(name = "version")
private String version;
#Column(name = "variant")
private String variant;
#OneToMany(mappedBy = "message", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
private List<DataEntity> data;
DataEntity:
#Entity(name = "Data")
#Table(name = "data")
public class DataEntity {
#OneToOne
#JoinColumn(name = "messageId")
private MessageEntity message;
#OneToMany(mappedBy = "data", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
private List<SubDataEntity> subData;
SubDataEntity:
#Entity(name = "SubData")
#Table(name = "subData")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class SubDataEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, name = "sd_id")
private Integer id;
#OneToOne(cascade = CascadeType.REFRESH)
#JoinColumn(name = "dataId")
private DataEntity data;
#Column(name = "value")
private String value;

The failure was caused be cause the attribute "immediate" was set to true in the component, that was displaying and editing the value for.
After switching on process="#this" and process="#form".

Probably it was due to caching of first level..
Try evicting the cache when you are loading the data..
getEntityManager().getEntityManagerFactory().getCache().evictAll();

Related

Why Hibernate provides me only interceptor and I catch JdbcSQLIntegrityConstraintViolationException: NULL not allowed?

I would be very grateful to you for help.
I use Spring Boot 2.5.2.
DB: H2 with Liquibase
I need to change ticket History when attachment is removed.
This is my entities:
Ticket:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Ticket {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_owner_id", nullable = false)
private User owner;
#OneToMany(mappedBy = "ticket", orphanRemoval = true)
private List<Attachment> attachments;
#OneToMany(mappedBy = "ticket", orphanRemoval = true)
private List<History> history;
// other fields and relationships
}
User:
#Entity
#Table(name = "users")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "owner", orphanRemoval = true)
private List<Ticket> ownerTickets;
// other fields and relationships
}
Attachment:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Attachment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String filename;
#Column(columnDefinition = "bytea")
private byte[] file;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "ticket_id", nullable = false)
private Ticket ticket;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
History:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class History {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#CreationTimestamp
#Column(name = "changed_date", nullable = false, updatable = false)
private LocalDateTime changedDate;
private String action;
private String description;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "ticket_id")
private Ticket ticket;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
Services:
JpaAttachmentService:
#Service
#AllArgsConstructor
public class JpaAttachmentService implements AttachmentService {
private final AttachmentRepository attachmentRepository;
private final HistoryService historyService;
#Transactional
#Override
public void delete(Long id, Long ticketId, Long userId) {
var attachment = attachmentRepository.getByIdAndTicketIdAndTicketOwnerId(id, ticketId, userId);
var action = "File is removed";
var description = "File is removed: " + attachment.getFilename();
var ticket = attachment.getTicket();
var user = attachment.getUser();
var history = new History(null, LocalDateTime.now(), action, description, ticket, user);
historyService.save(history);
attachmentRepository.deleteByIdAndTicketIdAndTicketOwnerId(id, ticketId, userId);
}
}
When I try to 'historyService.save(history)' I catch:
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "USER_ID"; SQL statement:
update history set action=?, description=?, ticket_id=?, user_id=? where id=? [23502-200]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:459)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)
at org.h2.message.DbException.get(DbException.java:205)
at org.h2.message.DbException.get(DbException.java:181)
at org.h2.table.Column.validateConvertUpdateSequence(Column.java:374)
at org.h2.table.Table.validateConvertUpdateSequence(Table.java:845)
at org.h2.command.dml.Update.update(Update.java:176)
at org.h2.command.CommandContainer.update(CommandContainer.java:198)
at org.h2.command.Command.executeUpdate(Command.java:251)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:191)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:152)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
... 144 more
This is 'var user' debug:
picture
I can't understand why I get this Hibernate Interceptor but not entity. I confused when I see inside interceptor required UserID and when I see 'null' outside of it. Could you help me with this problem, please?
Thanks in advance for any help.
The exception message is quite informative regarding the error you are experiencing. Take a look at the following part:
NULL not allowed for column "USER_ID"; SQL statement: update history
set action=?, description=?, ticket_id=?, user_id=? where id=?
What you attempt to do is to save an instance of a History entity which without passing in a reference to a User object. Since your relation dictates that the user reference cannot be null:
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "user_id", nullable = false)
To fix this, either make sure that a non-null reference to a User object is passed in when inserting/updating the History reference, or modify your database constraint design to allow for null user references at the History entity.
My problem was related to test, but in the further development I faced it again. So I want to share my solution.
Maybe it will help you.
Pay attention to CascadeType! Cascading operations must be specified above the link to the child relation. In my case, I have to remove this relation or leave Ticket with CascadeType.PERSIST
Fixed class Attachment:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Attachment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String filename;
#Column(columnDefinition = "bytea")
private byte[] file;
#ManyToOne(fetch = FetchType.LAZY) // or cascade = CascadeType.PERSIST
#JoinColumn(name = "ticket_id", nullable = false)
private Ticket ticket;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}

Hibernate OneToMany mapping & Query generation : More than one row with the given identifier was found

I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
AreaDto
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH)
#JoinColumn(name = "AREA_ROWID")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "FESTIVAL", joinColumns = {
#JoinColumn(name = "AREA_ROWID", referencedColumnName = "AREA_ROWID")}, inverseJoinColumns = {
#JoinColumn(name = "FESTIVAL_ROWID", referencedColumnName = "FESTIVAL_ROWID")})
private Collection<ActionDto> festivalActions;
}
FestivalDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
ActionDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background:
I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto#280856b5
This mapping does not make much sense. You can't map festivalActions this way because there is no way to persist the state properly through such a mapping. Also festival in AreaDto should be mapped by the area in FestivalDto. Try the following instead:
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
public Collection<ActionDto> getFestivalActions() {
return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
}
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}

Child entity not deleting on removal of parent entity

When deleting a parent entity I also want to remove the associated child entities (from the database). I have tried to make use of cascade on remove as seen below but I must be doing something incorrectly.
When calling remove on the parent entity object, I recieve the error message: "The entity is still referenced elsewhere in the database". I can confirm that the only place where the entity is referenced elsewhere in the database is in the two tables below (if I manually delete the child row from the database, the remove call on the parent works fine). I have been reading about entity objects and trying different things for the last 9 hours. What am I doing wrong?
Here is my parent table:
#Entity
#Table(name = "TURTLE_LOOKUP")
public class TurtleLookup implements Serializable
{
#Basic(optional = false)
#Column(name = "TURTLEID")
private int turtleid;
#Basic(optional = false)
#Column(name = "TURTLE")
private String turtle;
#OneToMany(mappedBy = "turtleType", cascade = CascadeType.REMOVE)
List<TurtleReview> turtleReviews;
...
}
Here is my child table:
#Entity
#Table(name = "TURTLE_REVIEW")
public class TurtleReview implements Serializable
{
#Column(name = "TURTLE_REVIEW_ID")
private int turtleReviewId;
#Column(name = "TURTLE_YEAR")
private int turtleYear;
#ManyToOne(cascade = CascadeType.REMOVE, optional = false)
#JoinColumn(name = "TURTLE_ID", referencedColumnName = "TURTLEID")
private TurtleLookup turtleType;
#Column(name = "IS_COMPLETE")
private short isComplete;
...
}
EDIT/UPDATE:
If I change CascadeType.REMOVE to CascadeType.ALL, the TurtleReview entities are successfully deleted from the database when deleting the parent TurtleLookup entity object. However, when calling the below function to create a new TurtleReview entity object, JPA tries to insert a new TurtleLookup entity in to the database, which throws the exception: "Entry already resides within the DB. Transaction rolled back". Below is the code executed when creating a new TurtleReview entity.
public void setDatasetReviewComplete(TurtleLookup turtle, Short year, boolean isComplete)
{
TurtleReview turtleReview = getTurtleReview(turtle, year);
if (turtleReview == null)
{
turtleReview = new TurtleReview();
turtleReview.setTurtleYear(year)
turtleReview.setTurtleType(new a.b.entity.TurtleLookup(turtle.getId(), turtle.getValue()));
}
turtleReview.setIsComplete(isComplete ? (short)1 : 0);
entityManager.persist(turtleReview);
}
try change cascade value to all or all-delete-orphan
#OneToMany(mappedBy = "turtleType", cascade = CascadeType.REMOVE)
List<TurtleReview> turtleReviews;
...
}
There might be an issue with your domain model, a part that is left out in the question. Do you possibly have circular cascades? If you have a circle of cascades and some of them are CascadeType.REMOVE and some are CascadeType.PERSIST, then Hibernate (not sure about other JPA implementation) will just do.... nothing when you call the remove() method. Without an error or exception message.
Try with hibernate #Cascade annotation:
#Cascade(value = CascadeType.ALL)
#OneToOne(mappedBy = "turtleReview") // mappedBy name of TurtleRewiew object field in TurtleLookup entity class
private TurtleLookup turtleType;
If your relationship is oneToOne you can't have oneToMany to the other side and you can't have List<TurtleReview>. If your relationship is oneToMany then your entities will be for example:
#Entity
#Table(name = "TURTLE_LOOKUP")
public class TurtleLookup implements Serializable
{
#Basic(optional = false)
#Column(name = "TURTLEID")
private int turtleid;
#Basic(optional = false)
#Column(name = "TURTLE")
private String turtle;
#OneToMany(mappedBy = "turtleType") // or add cascade = javax.persistence.CascadeType.ALL and remove #Cascade if you are not using hibernate
#Cascade(value = CascadeType.ALL)
List<TurtleReview> turtleReviews;
...
}
#Entity
#Table(name = "TURTLE_REVIEW")
public class TurtleReview implements Serializable
{
#Column(name = "TURTLE_REVIEW_ID")
private int turtleReviewId;
#Column(name = "TURTLE_YEAR")
private int turtleYear;
#ManyToOne
#JoinColumn(name = "TURTLE_ID", referencedColumnName = "TURTLEID")
private TurtleLookup turtleType;
#Column(name = "IS_COMPLETE")
private short isComplete;
...
}

Use Hibernate Formula annotation for reference loading

I have 3 entities - Storage, Item and Relation. Storage has several Item entities and items are bound by Relation entities. And relations can bind items from different storage. For simplification let say I want to load relation by query and want to do it fast. So now I have 3 query - loading for Storage, loading for all Items under storage and load of relation (List<Relation> relations field).
Now I want to tell hibernate how to load Collection<Relation> extRelation field. I tried #Formula, #CalculatedColumn and different combination of #ManyToMany and #JoinFormula. But generated query is wrong (or ignores my query). Also I cannot use #OneToMany because of https://hibernate.atlassian.net/browse/HHH-9897 bug.
Latest Exception is:
Caused by: org.h2.jdbc.JdbcSQLException: Table
"TEST_STORAGES_TEST_RELATIONS" not found; SQL statement:
SELECT extrelatio0_.test_storages_storage_id AS test_sto1_2_0_
,extrelatio0_.extRelation_from_item_id AS extRelat2_3_0_
,extrelatio0_.extRelation_to_item_id AS extRelat3_3_0_
,relation1_.from_item_id AS from_ite1_1_1_
,relation1_.to_item_id AS to_item_2_1_1_
,relation1_.relation_id AS relation3_1_1_
,relation1_.storage_id AS storage_4_1_1_
FROM test_storages_test_relations extrelatio0_
INNER JOIN test_relations relation1_ ON extrelatio0_.extRelation_from_item_id = relation1_.from_item_id
AND extrelatio0_.extRelation_to_item_id = relation1_.to_item_id
WHERE extrelatio0_.test_storages_storage_id = ?
My entities:
#Entity
#Table(name = "test_storages")
public class Storage {
#Id
#Column(name = "storage_id")
private BigInteger storageId;
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, targetEntity = Item.class)
#JoinColumn(name = "storage_id", updatable = false)
#MapKey
private List<Item> items;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, targetEntity = Relation.class)
#JoinColumn(name = "storage_id", updatable = false)
#Fetch(org.hibernate.annotations.FetchMode.SELECT)
private List<Relation> relations;
#ManyToMany()
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula =
#JoinFormula(
value = "(select dep.from_item_id, dep.to_item_id from test_relations dep where dep.storage_id = ?)"
)
)
})
private Collection<Relation> extRelation;
}
#Entity
#Table(name = "test_items")
public class Item {
#Id
#Column(name = "item_id")
private BigInteger itemId;
private String name;
#Column(name = "storage_id")
private BigInteger storageId;
}
#Entity
#Table(name = "test_relations")
public class Relation {
#Column(name = "relation_id")
private BigInteger relationId;
#Column(name = "storage_id")
private BigInteger storageId;
#EmbeddedId
private RelationPK pk;
}
#Embeddable
public class RelationPK implements Serializable {
#Column(name = "from_item_id")
private BigInteger fromItemId;
#Column(name = "to_item_id")
private BigInteger toItemId;
}
All sources available on https://github.com/ainlolcat/test_hibernate_formula

Can't delete element from #OneToMany collection

i have an entity Entity1 that have one to many relation with Entity2 as follows:
1- Entity1:
#Entity
#Table(name = "Entity1", catalog = "mydb")
public class Entity1 implements java.io.Serializable {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "entity1", cascade = javax.persistence.CascadeType.ALL)
#OrderBy("id")
private Set<Entity2> collection = new HashSet<Entity2>(
0);
}
2- Entity2: (equals and hashcode method overridden)
#Entity
#Table(name = "entity2", catalog = "advertisedb")
public class Entity2 implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "pkid", unique = true, nullable = false)
#Basic(fetch = FetchType.EAGER)
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "fk_entity1", nullable = false)
private Entity1 entity1;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "apid", nullable = false)
private Entity3 entity3;
}
3- Here's how i am removing the entity from the collection:
entity1Obj.getEntity2().remove(entity2);
log.debug("size after remove: "+ entity1Obj.getEntity2().size()); // size decreases correctly, so the entity is removed from the collection
entity1Dao.updateEntity1(entity1);
4- DAO method:
public void updateEntity1(Entity1 entity1) {
getCurrentSession().update(getCurrentSession().merge(entity1));
}
Problem: what i get in the console, is a select query for the entity2 that should be removed, and no delete query, and nothing is getting deleted.
please advise how to fix this issue.
i replaced cascade = CascadeType.ALL with orphanRemoval = true and it works fine now.

Categories

Resources