JPA: ensure that child objects are replaced on update - java

There is a persistent class that has a collection of child elements (setters, getters etc. removed from the listing):
#Entity
#Table(tableName = "...")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "myEntity")
private List<DateValue> values;
}
The child class is in fact just a tuple of LocalDate and BigDecimal:
#Entity
#Table(name = "my_entity_date_values")
public class DateValue implements Serializable {
LocalDate date;
BigDecimal value;
#ManyToOne
#JoinColumn(name = "my_entity_id")
MyEntity myEntity;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
The problem is that when a new list of (transient) values are created and set with the setter, new entities are created instead of replacing the existing:
List<DateValue> values = someDto.getSomeValues().entrySet().stream()
.map(entry -> new DateValue(entry.getKey(), entry.getValue(), myEntity))
.collect(Collectors.toList());
MyEntity.setValues(values);
What can be done to avoid it and ensure that old values are either updated or replaced by new ones?

The solution is to replace #Entity with #Embeddable in the case of DataValue and mark the list in the owning class (MyEntity) as #ElementCollection.
#Entity
#Table(tableName = "...")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ElementCollection
#CollectionTable(name = "my_entity_date_values")
private List<DateValue> values;
}
#Embeddable
public class DateValue implements Serializable {
LocalDate date;
BigDecimal value;
}

Related

JPA Embeddable bidirectional

What's the correct way to create bidirectional 1to1 mapping using Embeddable annotation? This one throws error
"EmpId has no persistent id property: Emp.id"
#Entity
public class Per implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "per_id")
private Long id;
#OneToOne
#JoinColumn(name = "per_id", referencedColumnName = "emp_id")
private Emp emp;
}
#Embeddable
public class EmpId implements Serializable {
private static final long serialVersionUID = 1L;
#OneToOne(mappedBy = "emp")
private Per per;
}
#Entity
public class Emp implements Serializable {
#EmbeddedId
private EmpId id;
}
I'd like to operate entities like
per.getEmp();
emp.getId().getPer();

Not able to delete in #OneToMany relationship spring data jpa

In my spring boot project, I have one LineItem entity below is the code
#Entity
#Table(name = "scenario_lineitem")
#Data
#NoArgsConstructor
public class LineItem implements Cloneable {
private static Logger logger = LoggerFactory.getLogger(GoogleConfigConstant.class);
#Id
#GeneratedValue(strategy = IDENTITY)
private BigInteger lineItemId;
#Column
private String name;
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "line_item_meta_id")
private List<QuickPopValue> quickPopValues;
}
Another entity is
#Entity
#Table(name = "quick_pop_value")
#Data
#NoArgsConstructor
public class QuickPopValue implements Cloneable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "quick_pop_value_id", columnDefinition = "bigint(20)", unique = true, nullable = false)
private BigInteger quickPopValueId;
#Column(name = "column_name")
private String columnName;
#Column(name = "value")
private String value;
#Column(name = "formula", columnDefinition = "longtext")
private String formula;
}
Now I am trying to delete QuickPopValue one by one but it's not getting deleted and not getting any exception as well.
Below is the delete code :
List<QuickPopValue> quickPopValues = sheetRepository.findByColumnName(columnName);
for (QuickPopValue qpValue : quickPopValues) {
quickPopValueRepository.delete(qpValue);
}
Such behavior occurs when deleted object persisted in the current session.
for (QuickPopValue qpValue : quickPopValues) {
// Here you delete qpValue but this object persisted in `quickPopValues` array which is
quickPopValueRepository.delete(qpValue);
}
To solve this you can try delete by id
#Modifying
#Query("delete from QuickPopValue t where t.quickPopValueId = ?1")
void deleteQuickPopValue(Long entityId);
for (QuickPopValue qpValue : quickPopValues) {
quickPopValueRepository.deleteQuickPopValue(qpValue.getQuickPopValueId());
}

Hibernate one to one criteria fetching

I am using hibernate one to one mapping between car and person table. But a person might have car and might not have a car. now while fetching the records from the person table using hibernate criteria , I want to fetch only those persons who have a car, i.e only those entries in person table for which a corresponding entry in car table exists. How this can be done using hibernate criteria/alias?
Below is the piece of code. kindly help with the criteria or alias that has to written to fetch result:
#Getter
#Setter
#Entity
#Table(name = "Person")
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Person_ID")
private Long personId;
#OneToOne(mappedBy = "personAsset", cascade = CascadeType.ALL)
private Car car;
}
#Getter
#Setter
#Entity
#Table(name = "Car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Car_ID")
private Long carId;
#OneToOne
#JoinColumn(name = "Person_ID")
private Person personAsset;
}
what you are looking for is the cascadeType orphanRemoval=true on the #OneToOne annotation.
here is your class how would look like :
#Getter
#Setter
#Entity
#Table(name = "Car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Car_ID")
private Long carId;
#OneToOne(fetch=FetchType.EAGER , cascade=CascadeType.ALL, orphanRemoval=true)
#JoinColumn(name = "Person_ID")
private Person personAsset;
}
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> person = query.from(Person.class);
Predicate predicate = cb.isNotNull(person.join("car"));
predicates.add(predicate );

Loading DTO with collection

#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)

JAXB Unmarshalling Entities - References null

I have 2 mapping classes (analogue to JPA classes):
AElement.java
#XmlType(propOrder = {"name", "children", })
#XmlRootElement(name = "a")
#XmlAccessorType( XmlAccessType.PROPERTY)
public class AElement implements Serializable {
private String name;
private List<BElement> children;
#XmlElement(name = "metadatum")
public List<BElement> getChildren(){
return children;
}
...
}
BElement.java
#XmlRootElement(name = "b")
#XmlType(propOrder = {"name"})
public class BElement implements Serializable{
private String name;
private AElement parent;
...
}
A and B are in a OneToMany relation. The XML should look like this:
<A>
<B></B>
<B></B>
</A>
If I unmarshal the xml, map it to my JPA classes and persist it to my database everything is
stored correctly except my references. This means that B is stored without a foreign key to A in the database.
I'm using JPA with Hibernate. Following my JPA classes:
A.java
#Entity
public class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "parent")
private List<B> children;
public List<B> getChildren(){
return children;
}
...
}
B.java
#Entity
public class B implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column
private String name;
#ManyToOne(optional = true)
#JoinColumn(name = "a_id", referencedColumnName = "id")
private A parent;
...
}
Looks like I have to assign the appropriate A to each B. This solves my problem.
a.getB().forEach(b -> b.setA(a));
I don't know If it is a good workaround? Especially because I have other entities that are children of B.
Found a solution:
Method afterUnmarshal in class BElement.java
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.a = (AElement)parent;
}

Categories