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;
}
Related
For example i have
entities
#Entity
public class A{
#Id
Long Id;
...
}
#Entity
public class B{
#Id
Long Id;
...
}
#Entity
#IdClass(ABId.class).
public class AB{
#Id
#ManyToOne
private A a;
#Id
#ManyToOne
private B b;
private boolean state;
}
Class for composite primary key:
public ABId implements Serializable{
Long a;
Long b;
.........
}
and i want to get from class A something like this select * from AB ab where ab.a_id=1; ( id from A object)
i did such mapping in class A
#OneToMany(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="A_id", referencedColumnName="id"),
#JoinColumn(name="B_id", referencedColumnName="id")
})
List<AB> listAB;
but it does nothing i alawys get empty list.
Solved,
I did mapping
#OneToMany(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="A_id", referencedColumnName="id")
})
List<AB> listAB;
and it works like it should.
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;
}
#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)
Hi I have 3 entities.
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
...
private List<BC> bcList;
}
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
...
}
#Entity
public class C {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
...
}
These entities are linked by ManyToMany associations each other. It means I need an association table which look like a_b_c(id_a,id_b,id_c).
So I've created an AssociationOverrides which work exactly as I want.
#Embeddable
public class ABCPK implements Serializable {
#ManyToOne
#JoinColumn(name = "id_a")
private A a;
#ManyToOne
#JoinColumn(name = "id_b")
private B b;
#ManyToOne
#JoinColumn(name = "id_c")
private C c;
...
}
#Entity
#Table(name = "a_b_c")
#AssociationOverrides({
#AssociationOverride(name = "pk.a",
joinColumns = #JoinColumn(name = "id_a",referencedColumnName = "id")),
#AssociationOverride(name = "pk.b",
joinColumns = #JoinColumn(name = "id_b",referencedColumnName = "id")) ,
#AssociationOverride(name = "pk.c",
joinColumns = #JoinColumn(name = "id_c",referencedColumnName = "id")) })
public class ABC{
#EmbeddedId
private ABCPK pk=new ABCPK();
#Transient
public A getA() {
return pk.getA();
}
public void setA(A a) {
this.pk.setA(a);
}
...
}
public class BC{
private B b;
private List<C> cList;
}
However I would like to retrieve List bcList and List cList from my association table but I really don't know how can I do it. I've already tried #JoinFormula but I prefere to get it working using jpql only. Could you please put me in the right way ?
Thanks
Since I haven't got any help I've made up my mind to populate my map myself and re populate abcList before persist and update . However I really don't like this since it's not performance and memory friendly. (about 1*20*70 items in the abcList)
#Entity
public class A {
...
#OneToMany
private List<ABC> abcList;
private Map<B,List<C>> bcMap;
private Map<B,List<C>> buildBCmap(){
bcMap = new HashMap<>();
if(abcList!=null){
for(ABC abc:abcList){
if(! bcMap.containsKey(abc.getB()){
bcMap.put(abc.getB(),new ArrayList<>());
}
bcMap.get(abc.getB()).add(abc.getC());
}
}
return bcMap;
}
public Map<B,List<C>> getBcMap(){
if(bcMap==null){
buildBCmap();
}
return bcMap;
}
#PrePersist
#PreUpdate
public void buildAbcList(){
if(bcMap!=null){
abcList=new ArrayList<>();
for(B b: bcMap.keySet()){
for(C c: bcMap.get(b)){
abcList.add(new ABC(this,b,c));
}
}
}
}
}
Does anyone have a better or faster way to populate a map from associationOverride entity ?
So I am getting the exception:
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: se.mulander.cosmos.movies.model.Cast.starredIn in se.mulander.cosmos.movies.model.ExtendedMovie.cast
But I can't really figure out why.
The two objects that I am going to map are:
#Entity
#Table(name = "cast")
#ApiModel(description = "A cast member that has been part of making the movie")
public class Cast
{
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "movie_id")
public ExtendedMovie starredIn;
}
and
#Entity
#Table(name = "extended_movie")
public class ExtendedMovie
{
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "starredIn", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
public List<Cast> cast = new ArrayList<>();
}
I have stripped them of some other properties, but in essence this is the relationship that is not working.
So what I don't get is why it says that it is an unknown property, as the property is public and hibernate shouldn't have any problems mapping it.
what is it that I am missing here?
Try something like:
ExtendedMovie :
#Entity
public class ExtendedMovie implements Serializable {
private static final long serialVersionUID = 6771189878622264738L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "cast_id", referencedColumnName = "id")
private Set<Cast> cast;
public Set<Cast> getCast() {
return cast;
}
public void setCast(Set<Cast> cast) {
this.cast= cast;
}
}
Cast:
#Entity
#ApiModel(description = "A cast member that has been part of making the movie")
public class Cast implements Serializable {
private static final long serialVersionUID = 6771189878622265738L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
//Remove extendedmovie from here
//other property getter and setters here
}
This will establish a one-to-many relationship between ExtendedMovie and Cast.