I'm attempting to build an application level view with Spring JPA. I'm also using lombok.
There is an existing client already using this web service which can't be changed at this time.
We have a new client which needs the same data and even more now. So I thought this could
be addressed with an application level view so to speak.
The basic scope of the problem is I have 3 entities: A, B and C.
A and C are entities pointed at the same table. C has more properties than A.
Both C and A have references to B.
#Entity
#Getter
#Setter
#Table(name="Foo", schema="dbz")
public class A {
#Id
#Column(name="FOO_I")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(fetch =FetchType.EAGER, cascade= CascadeType.ALL)
#JoinColumn(name="FOO_I",nullable=false)
private Set<B> items = new HashSet<B>();
#Column(name="X")
private String x;
}
#Entity
#Getter
#Setter
#Table(name="Bar", schema="dbz")
public class B {
#Id
#Column(name="BAR_I")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name="Y")
private String y;
#Column(name="Z")
private int z;
}
#Entity
#Getter
#Setter
#Table(name="Foo", schema="dbz")
public class C {
#Id
#Column(name="FOO_I")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(fetch =FetchType.EAGER, cascade= CascadeType.ALL)
#JoinColumn(name="FOO_I",nullable=false)
private Set<B> items = new HashSet<B>();
#Column(name="X")
private String x;
#Column(name="MoreData")
private String moreData;
//And much more other data...
}
When I comment out the #Entity and #Table on class C and rebuild, I don't get an exception in my validation tests.
When I leave the #Entity and #Table annotations on class C, I get the following exception:
Caused by: org.hibernate.MappingException: Duplicate property mapping of _items_FOO_IBackref found in com.acme.Bar
If I rename items to bars in C, then I get the following exception:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.acme.Bar column: FOO_I (should be mapped with insert="false" update="false")
Is there any way to get hibernate to accept 2 java classes backed by the same table which relate to another entity? I'm not sure if I am missing something here or if this is a framework limitation.
You are using component mapping so use #Embedded annotation for the A class without using #Entity, #Id and #Table annotations here is a useful link;
http://www.dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-component-using-annotations-1.html
Related
I'm using Spring and JPA (Hibernate with MySQL) and Lombok also.
Hi have this part of my entities:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "entitya")
public class EntityA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ea_id")
Long id;
....
#ManyToOne
#JoinColumn(name="g_id", nullable=false)
private Group group;
....
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "group")
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="g_id")
private Long id;
#OneToMany(mappedBy="group")
private List<EntityA> enitiesA = new ArrayList<>();
...
}
I implemented also the repository extends JPARepository.
Into my controllers, if I try to retrieve an EntityA by Id I obtain this exception:
failed to lazily initialize a collection of role: com.mytest.entity.Group.enitiesA, could not initialize proxy - no Session
For me it's strange because I need to retrieve only the object. I not use some getter methods on this. So, in theory, using the default fetch types, I don't need to have also the group list.
What's wrong?
Are you debugging your object with toString()?
In case it could be an error caused by the #Data annotation.
The generated toString() method contains all fields, so it might call the enitiesA variable, producing the lazy initialization error.
https://mdeinum.github.io/2019-02-13-Lombok-Data-Ojects-Arent-Entities/
Likely it's because you're accessing group.enitiesA outside of the transactional boundaries. If you want to do this, you can eager fetch them by adding eager fetch type to your OneToMany mapping such as
#OneToMany(mappedBy="group", fetch = FetchType.EAGER)
This will load the entire object graph when the parent is loaded.
If you still want to do lazy loading, look to encapsulate all of the calls into the children under the session that loaded the parent.
After searching an answer, I have seen the #PrivateOwner annotation but it doesn't solve my issue.
I'm using EclipseLink
here is the issue:
I have 2 Entities related with a 1:n bidirectional relationship.
A ComponentEntity can be related to one TransferDetailsEntity
A TransfertDetailsEntity is related to one or multiple ComponentEntity.
what I want to achieve is: when I delete the last Component referencing a TransfertDetails, this TransfertDetails should be deleted.
Same if I change the last reference to a TransfertDetails.
in short : As soon as a TransfertDetails is not referenced by any Component, it should be deleted.
As a workaround I call this method :
#Override
public void removeOrphanTransfer() {
for (TransferDetailsEntity transfer : transferDetailsRepository.findAll()) {
if (transfer.getComponents().isEmpty()) {
transferDetailsRepository.delete(transfer);
}
}
}
That works but it's not really efficient since it search through the entire table. and it's quite ugly...
here is the (simplified) code for Entities :
TransfertDetailsEntity:
#Entity
#Table(name = TransferDetailsEntity.TABLE_NAME)
#Getter
#Setter
#NoArgsConstructor
public class TransferDetailsEntity extends AbstractEntity {
[...]
#Id
#Column(name = ID, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
[...]
#OneToMany(mappedBy = "transferDetails")
private List<ComponentEntity> components;
[...]
}
ComponentEntity:
#Entity
#Table(name = ComponentEntity.TABLE_NAME, uniqueConstraints = #UniqueConstraint(name = ComponentEntity.TABLE_NAME
+ AbstractEntity.SEPARATOR + AbstractEntity.CONSTRAINT,
columnNames = { ComponentEntity.COLUMN_NAME_SERIAL, ComponentEntity.COLUMN_NAME_TYPE }))
#Getter
#Setter
#ToString(callSuper = true, exclude = { "parent" })
#NoArgsConstructor
public class ComponentEntity extends AbstractEntity {
[...]
#Id
#Column(name = ID, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
[...]
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = COLUMN_TRANSFER_DETAILS)
private TransferDetailsEntity transferDetails;
[...]
}
As mentioned earlier, #PrivateOwner on the #OneToMany annotation (in TransfertDetailsEntity) doesn't work...
any help appreciated
There is no automatic JPA or EclipseLink feature that will do this for you, your application will have to handle this.
The easiest I can think of is on removal of a ComponentEntity, get the referenced TransfertDetailsEntity and check its components list to see if it has other ComponentEntity references and remove it if it does not. You should be removing each ComponentEntity reference from the TransfertDetailsEntity.components list when you delete it anyway, so this list should be up to date and not incur any database hits.
I saw similar questions, but answers weren't helpful. So, i get this error:
Use of #OneToMany or #ManyToMany targeting an unmapped class: com.podro.model.Journey.roadWay[com.podro.model.RoadElement]
I'm trying to create List with objects of RoadElements (which is interface for class Point and Section). There is any other way to do it? From what i know, i guess that is the only way to create proper mapping for this classes, and have list of this elements.
#Entity
#Table(name="Journey")
public class Journey {
// Some other fields
#Column(name="road_way")
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private List<RoadElement> roadWay;
}
#MappedSuperclass
public interface RoadElement {}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Point")
public class Point implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String name;
#Column(name="time_in_days")
private int timeInDays;
private Rate rating;
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Section")
public class Section implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name="section_name" , length=100)
private String sectionName;
#Column(name="time_in_days")
private int timeInDays;
#Column(name="kind_of_transport")
private Locomotion kindOfTransport;
}
Thanks for answers, I would be very grateful for help!
Associations are between entities. RoadElement is not an entity. It's an interface.
You may not do what you're trying to do. Hibernate needs to know the type of the entities contained in roadWay.
So, RoadElement should be a class, annotated with #Entity, having an ID that uniquely identifies a RoadElement among all the road elements (sections, points, etc.)
Section and Point should extend from RoadElement, and should NOT have their own ID, since it's inherited from RoadElement.
I have a problem with OneToMany annotation
#Entity
#Table(name = "RESULT_HISTORY")
public class ResultHistoryImpl implements ResultHistory, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany
private final Set<Game> games = new HashSet<>();
...
}
and class
#Entity
#Table(name = "GAME")
public class GameImpl implements Game, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
...
}
I have more fields, setters/getters and constructors
I use spring and in my configuration files I have packagesToScan where I put packages of those classes
The problem is that I get org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class
I read many topics on stackoverflow, so the problem isn't using org.hibernate.annotation etc
What you tried is not possible. Game is an interface and you cannot map an interface to DB, because hibernate does not know which implementation to use. But what you can do, is
use in all your entity classes the GameImpl class directly. I mean correct your ResultHistoryImpl class to this: #OneToMany private final Set<GameImpl> games = new HashSet<GameImpl>();
If you want to have another abstraction, add an abstract class AbstractGame implements Game and annotate it with #Entity and use it instead of the Game interface.
Use #MappedSuperclass annotation on Game.
For #OneToMany you have to provide a mappedBy property which points to a field name in the other class. In your case:
public class ResultHistoryImpl {
...
#OneToMany(mappedBy = "resultHistory")
private final Set<GameImpl> games = new HashSet<>();
}
public class GameImpl {
#ManyToOne
#JoinColumn(name = "result_history")
private ResultHistoryImpl resultHistory;
}
I have searched and found similar issues, but they don't quite seem to be the same problem as
Why am I getting this NullPointer exception?
OneToOne Mapping with hibernate/JBoss/Seam
ANN-613 - NPE when mappedBy property is wrong on a #OneToOne
ANN-558 - #OneToMany(mappedBy="") can not recognize properties in parent classes
Hibernate Users - NPE with #Id on #OneToOne
I have a few entities mapped like this:
Person
|
+--User
I want to add a new entity PersonPartDeux with a OneToOne mapping to Person. The resulting mapping should look something like this:
Person + PersonPartDeux
|
+--User
When I do so, a NullPointerException is thrown while trying to load the mapping:
java.lang.NullPointerException
at org.hibernate.cfg.OneToOneSecondPass.doSecondPass(OneToOneSecondPass.java:135)
How do I specify the mapping so I can avoid this exception?
Here's my code:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Person implements Serializable
{
#Id
#GeneratedValue
public Long id;
#Version
public int version = 0;
public String name;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
public PersonPartDeux personPartDeux;
}
#Entity
public class PersonPartDeux implements Serializable
{
#Id
#GeneratedValue(generator = "person-primarykey")
#GenericGenerator(
name = "person-primarykey",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "person")
)
public Long id = null;
#Version
public int version = 0;
#OneToOne(optional=false, mappedBy="person")
public Person person;
public String someText;
}
#Entity
#PrimaryKeyJoinColumn(name = "person_Id")
public class User extends Person
{
public String username;
public String password;
}
As for why I'm bothering, I need both the inheritance and the OneToOne mapping to solve different known issues in my application.
Attach the Hibernate source to your project, so you can click thru or 'Open Type' (Ctrl-Shift-T in Eclipse) to view the OneToOneSecondPass source.
Seeing the source, will give you a clear indication as to what needs to be specified.
In my source (Hibernate 4.1.7), line 135 is
propertyHolder.addProperty( prop, inferredData.getDeclaringClass() );
However you're probably using an earlier version.
Looking at the mappings, I'm suspicious of the #OneToOne definition -- mappedBy="person".
#OneToOne(optional=false, mappedBy="person")
public Person person;
What does it usefully mean, to map an association property by itself? Hibernate already knows the property is a OneToOne -- you just told it that.
Pointing the underpinning mapping/ FK of the property, at itself.. probably isn't actually telling Hibernate any correct or useful information.
Here's an example from the HB dosc, perhaps showing better how to do what you want:
#Entity
class MedicalHistory implements Serializable {
#Id Integer id;
#MapsId #OneToOne
#JoinColumn(name = "patient_id")
Person patient;
}
#Entity
class Person {
#Id #GeneratedValue Integer id;
}
Source: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/
(3.5 docs off JBoss site.)
Cheers, hope this helps.