How to have Zero To Many relationship in Hibernate - java

I have a class called Member that has a few #OneToMany relationships with other classes. I can insert only one Member with empty OneToMany relationships. Upon inserting the second Member I receive following exception.
ERROR: Duplicate entry '' for key 'UK_7pn834d04yft1rkvpqf0viuyc'
Code
#Entity
public class Member {
#Id
#Column(name = "username", nullable = false, unique = true)
private String email;
#OneToMany(mappedBy = "friend")
private Set<Friendship> friends;
....
To insert Member, I am adding empty set of friends to it. I suspect this and suppose need to have ZeroToMany relationship but could not find its annotation in Hibernate documentation.
Update
Member member = new Member(email, new HashSet<Friendship>());
Constructor
public Member(String email, Set<Friendship> friends) {
super();
this.email = email;
this.friends = friends;
}
toString
member:Member [email=jack.moore1#outlook.com , friends=[]]
console
insert
into
Member
(username)
values
(?)

There is no ZeroToMany relationship. That doesn't exist and doesn't have sense. You can't connect 0 entities with many entities.
You should use #OneToMany relationship here, and you should initialize friends collection
private Set<Friendship> friends = new HashSet<Friendship>();
if you didn't do that in some constructor which is not shown here.
I would suggest to turn on show_sql hibernate property so you can have more details about the problem.

Related

Spring repository saveAll inserting duplicate rows for mapped entity

I am trying to insert a list of entities which have one to one relation to another entity. It is possible that the one to one mapped object would be same for many parent entity. I am expecting that the same child entity is referred in foreign keys of parent, but actually duplicate rows are getting created. Here are my Entities.
#Builder
#Entity
public class PaymentInfoType1 {
#Id
Long id;
LocalDate date;
#Column(precision = 15, scale = 2)
BigDecimal amount;
String reference;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "account", referencedColumnName = "id")
Account account;
}
#Builder
#Entity
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Account {
#Id
Long id;
#EqualsAndHashCode.Include
String name;
#EqualsAndHashCode.Include
String accountId;
}
I am creating a list of PaymentInfoType1 based on the information received from a different system. Each PaymentInfoType1 get created along with its Account, which could have exactly the same info but different objects in realtime.
When i do:
PaymentInfoType1 first = // Created with some logic
Account account1 = // name = sample & accountId = 123
first.setAccount(account1);
PaymentInfoType1 second = // Created with some logic
Account account2 = // name = sample & accountId = 123
second.setAccount(account2);
// Both the above its own account object but the field have exactly same values.
List<PaymentInfoType1> list = List.of(first, second);
repo.saveAll(list);
I was expecting that there will be two rows in PaymentInfoType1 table and one in Account, but found that Account also has two rows. Looks like Equals and HashCode does not have any effect in this case.
How can handle this to not insert duplicate rows when the mapping objects are similar by equals/hashcode.
JPA does nothing with #EqualsAndHashcode (that just generates class methods equals and hashCode).
JPA identifies entities by entity id annotated with #Id (or #EmebeddedId) and this id is also something that can be implemented and checked - and usually also generated (like some db sequence) - in the database level.
If you want to use Account identified by name and accountId on JPA side you need to use #EmbeddedId and #Embeddable and get rid of #Id. This would be something like:
#Embeddable
public class AccountId {
String name;
String accountId; // maybe needs renaming...
}
and then in the Account:
#EmbeddedId
AccountId accountId;
See this for example

Relationship oneToMany using hibernate and entitymanagerfactory java

I have a problem while building a OneToMany relationship between two entities. I have a user who can have multiple Properties. I want to define this relationship and create tables in MySql however when I run it it gives me Exception in thread "main" java.lang.NullPointerException
at start.ApplicationStart.main(ApplicationStart.java:44)
and also there are 2 more columns in the user table in mysql "user_id" and city. Now what I defined so far in my code is:
in class user I have the following fields:
#Id
private String id;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL) //mapped by variable in class user
private List<Properties> properties;
and in class Properties I have:
#Column(name = "city")
private String city;
#Column(name = "address", nullable = false)
private String address;
#ManyToOne
#JoinColumn(name="user_id")// , nullable=false
private User user;
I don't understand why I have the extra columns in the user table. Also I am using entity manager for both tables. Any hint would be great

Hibernate - Save Child if not persist

i have a threading hibernate problem.
I have a ManyToOne Relation with a track whitch include two city objects.
Through the thread it is possible that my trackDao get many trackEntitys to persist witch include the same city (zipcode is unique).
After the trackDao persist the first entity i get a "duplicate entity exception".
Is it possible to configure via annotaition to force a get bevore save to get the existing childId and resume parrent persisting?
Thx
thx for your time.
I try a manual get before i try to save a city to get a possibly already existing city (with the same zipcode) from db.
Now a trip includes two cities.
One city witch is allready stored in db (objectId != null)
and an other city witch have to get persist (objectId == null)
Although hibernate now have to know that city1 is already persist an only city2 have to get persist in city- table i get a "duplicate entity exception". Hibernate will store the existing city again.
To slove that I overrive the generic save method of my tripDao and handle the city persisting manual. -> just save city if city.getId() == null.
So I get a not stored trip object witch includes two already stored citys. If i try to persist that trip object I get the hibernate message
"Error while persist trackjavax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: de....CityEntity"
I belive I had something configured totaly wrong.
Pls help.
I dont want to overrive the save method and store realations manualy. In my opinion that have to works by default, or not?
#Entity
#Table(name = "tracks", uniqueConstraints = #UniqueConstraint(columnNames = { "city1_id", "city2_id" }))
#SessionScoped
public class TrackEntity extends BaseEntity {
private static final long serialVersionUID = 4696847902782174082L;
#ManyToOne(targetEntity = CityEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, optional=false)
#JoinColumn(name="city1_id")
private CityEntity city1;
#ManyToOne(targetEntity = CityEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, optional=false)
#JoinColumn(name="city2_id", referencedColumnName="id")
private CityEntity city2;
private Integer distance;
#Entity
#Table(name = "cities")
#SessionScoped
public class CityEntity extends BaseEntity {
private static final long serialVersionUID = 8823384603378561475L;
private String name;
#Column(unique=true)
private String zipcode;
private String latitude;
private String longitude;
I think a track has two identical city . I had a same problem with Order ->* Product. But this was a bug in application where a Order can not have duplicate product. I don't know what is allowed in your case but I suggest you to Investigate City class and look for equals method. If you have java.util.List in Track class to hold cities then it is possible that that list have two element of same object (==).

Hibernate Annotations with a collection

I am trying to implement my model using hibernate annotations. I have 3 classes, image, person, and tags. Tags is a a table consisting of 4 fields, an id, personId, imageId, and a createdDate. Person has the fields name, id, birthdate, etc. My image class is defined as follows:
#Entity
#Table(name="Image")
public class Image {
private Integer imageId;
private Set<Person> persons = new HashSet<Person>();
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
public Integer getImageId() {
return imageId;
}
public void setImageId(Integer imageId) {
this.imageId = imageId;
}
#ManyToMany
#JoinTable(name="Tags",
joinColumns = {#JoinColumn(name="imageId", nullable=false)},
inverseJoinColumns = {#JoinColumn(name="personId", nullable=false)})
public Set<Person> getPersons() {
return persons;
}
public void setPersons(Set<Person> persons) {
this.persons = persons;
}
If I remove the annotations on the getPersons() method I can use the classes and add and remove records. I want to fetch all the tags with the image and I am trying to use a set. I keep getting the following error:
org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: com.exmaple.persons, no session or session was closed
Can someone please help me and let me know what I am doing wrong?
Thank you
This error message - which actually has nothing to do with your association mapping strategy or annotations - means that you have attempted to access a lazy-loaded collection on one of your domain objects after the Session was closed.
The solution is to either disable lazy-loading for this collection, explicitly load the collection before the Session is closed (for example, by calling foo.getBars().size()), or making sure that the Session stays open until it is no longer needed.
If you are not sure what lazy-loading is, here is the section in the Hibernate manual.
Thanks for the response matt. I am confused now. My query to retrieve the image looks like this:
public Image findByImageId(Integer imageId) {
#SuppressWarnings("unchecked")
List<Image> images = hibernateTemplate.find(
"from Image where imageId=?", imageId);
return (Image)images.get(0);
}
I thought that I can call the single hql query and if my mappings are correct it will bring back the associated data.
I was looking at this example at this link hibernate mappings:
2.2.5.3.1.3. Unidirectional with join table
A unidirectional one to many with join table is much preferred. This association is described through an #JoinTable.
#Entity
public class Trainer {
#OneToMany
#JoinTable(
name="TrainedMonkeys",
joinColumns = #JoinColumn( name="trainer_id"),
inverseJoinColumns = #JoinColumn( name="monkey_id")
)
public Set<Monkey> getTrainedMonkeys() {
...
}
#Entity
public class Monkey {
... //no bidir
} Trainer describes a unidirectional relationship with Monkey using the join table TrainedMonkeys, with a foreign key trainer_id to Trainer (joinColumns) and a foreign key monkey_id to Monkey (inversejoinColumns).

GAE Datastore: persisting referenced objects

I am trying to persist Java objects to the GAE datastore.
I am not sure as to how to persist object having ("non-trivial") referenced object.
That is, assume I have the following.
public class Father {
String name;
int age;
Vector<Child> offsprings; //this is what I call "non-trivial" reference
//ctor, getters, setters...
}
public class Child {
String name;
int age;
Father father; //this is what I call "non-trivial" reference
//ctor, getters, setters...
}
The name field is unique in each type domain, and is considered a Primary-Key.
In order to persist the "trivial" (String, int) fields, all I need is to add the correct annotation. So far so good.
However, I don't understand how should I persist the home-brewed (Child, Father) types referenced.
Should I:
Convert each such reference to hold the Primary-Key (a name String, in this example) instead of the "actual" object, so Vector<Child> offsprings; becomes Vector<String> offspringsNames;? If that is the case, how do I handle the object at run-time? Do I just query for the Primary-Key from Class.getName, to retrieve the refrenced objects?
Convert each such reference to hold the actual Key provided to me by the Datastore upon the proper put() operation? That is, Vector<Child> offsprings; becomes Vector<Key> offspringsHashKeys;?
I have read all the offical relevant GAE docs/example. Throughout, they always persist "trivial" references, natively supported by the Datastore (e.g. in the Guestbook example, only Strings, and Longs).
Please see google appengine docs following sections for more clear understanding (Relationships, Transactions)
Also read about detachable objects in JDO
For querying selective columns (or fields), read about fetchgroups in JDO
For your question You have several options:
Owned One to many relationship (the objects will be in same entity group) Here you can have a list of Child in your parent (Father class). This will place all objects in the same entity group. If you do not want to fetch the children every time you fetch Father, you can remove the children from the "default fetch group"
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
#PrimaryKey
#Persistent
private String name;
#Persistent
private int age;
#Persistent(mappedBy = "father", defaultFetchGroup = "false")
private List childern;
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {
#Persistent
#PrimaryKey
private String name;
#Persistent
private Father dad;
}
Unowned relationships where you store the keys instead of references:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
#PrimaryKey
#Persistent
private String name;
#Persistent
private int age;
#Persistent
private List childern;
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {
#Persistent
#PrimaryKey
private String name;
#Persistent
private Key dad;
}
In this case you will have to manage the referential integrity and also make sure they are in the same entity group if you have to update/add them in a single transaction
IMO, if I were modeling a real-world (Father-children) scenario, I'd go the "Owned relatinship" route, since, really, how many children a guy can have ;). Of course there is an additional question of how many fathers are you going to update at a time?
Hope this helps, cheers!
I have examples of creating parent/child relationships using GAE/JPA in my jappstart project. Take a look at how the authentication related entities are related to each other here.
One-to-One (see UserAccount.java and PersistentUser.java):
// parent
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private PersistentUser persistentUser;
// child
#OneToOne(mappedBy = "persistentUser", fetch = FetchType.LAZY)
private UserAccount userAccount;
One-to-Many (see PersistentUser.java) :
#OneToMany(mappedBy = "persistentUser", cascade = CascadeType.ALL)
private Collection<PersistentLogin> persistentLogins;
Many-to-One (see PersistentLogin.java):
#ManyToOne(fetch = FetchType.LAZY)
private PersistentUser persistentUser;
Also, note in the constructors how KeyFactory is used for entities with a parent versus without a parent.
#Id
private Key key;
// this entity has a parent
public PersistentUser(final Key key, final String username) {
this.key = KeyFactory.createKey(key, getClass().getSimpleName(), username);
...
}
// this entity does not have a parent
public UserAccount(final String username) {
this.key = KeyFactory.createKey(getClass().getSimpleName(), username);
....
}
Hopefully, this is helpful for you. I couldn't tell from the question whether you were using JPA or JDO.
If you have a reference to a Father in the Child and to the Children in the Father than you have the potential for inconsistency assuming that the relationship between Father and Child is two-way (ie. every Child's father should be in the list of Children for that Father). Only one of the two references is necessary.
Both solutions will work, but keeping the list of children in the father has two disadvantages:
Every access to the Father object will download the list keys to the child object. If there are many keys, this could cause unnecessary overhead.
I believe that GAE limits the size of a list to 5,000 items.

Categories

Resources