I'm using hibernate with manyToMany relation and I want to display data from database
Thank you in advance.
I get this errors:
database :
Here is the code :
Class EnseignerId :
#Embeddable
public class EnseignerId implements Serializable {
//id professeur
#Column(name="professeur_code")
private int code;
//id matiere
#Column(name="matiere_reference")
private String reference;
public EnseignerId() {
super();
}
//getters and setters...
Class Enseigner :
#Entity
#Table(name="Enseigner")
public class Enseigner {
#EmbeddedId
private EnseignerId id = new EnseignerId();
//id prof
#ManyToOne
#MapsId("code")
private Professeur professeur;
//id matiere
#ManyToOne
#MapsId("reference")
private Matiere matiere;
#Column(name="heures")
private int heures;
//constructor getters and setters...
Class Professeur:
#Entity
#Table(name="professeur")
public class Professeur {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="code")
private int code ;
#Column(name="nom")
private String nom;
#Column(name="prenom")
private String prenom;
...
#OneToMany(
mappedBy="professeur",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Enseigner> matieres; //List<Association> Class; //I followed a tutorial
//constructor getters and setters...
public List<Enseigner> getMatieres() {
return matieres;
}
Class Matiere :
#Entity
#Table(name="matiere")
public class Matiere {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="reference")
private String reference;
#Column(name="description")
String description;
#Column(name="volume")
int volume;
#OneToMany(
mappedBy= "matiere",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Enseigner> professeurs;
//constructor getters and setters...
getProfesseur() method :
public Professeur getProfesseur(int code) {
SessionFactory sessionFactory = getSessionFactory(); //static method
Session session = sessionFactory.openSession();
Professeur professeur = null;
try {
session.getTransaction().begin();
System.out.println("------------Calling getProfesseur()----------");
professeur = session.get(Professeur.class, code);
if(professeur != null) {
System.out.println(professeur);
}else {
throw new DAOException( "CODE INVALIDE!" );
}
}
catch(Exception e ) {
System.out.println(e.getMessage());
}
finally {
session.close();
}
return professeur;
}
Saving data and getting professors who don't have an Matiere work. but getting Matiere or professeur whose primary key exists in the join table Enseigner generate errors when I do something like :
Professeur prof =profDAO.getProfesseur(2); //*generates errors* //the professor with id=2 exists in database
System.out.println(prof);
List<Enseigner> enseigner = prof.getMatieres(); //*generates errors*...
List<Matiere> matieres = new ArrayList<>();
for(Enseigner ens : enseigner) {
matieres.add(ens.getMatiere());
System.out.println(ens);
}
/*for(Matiere mat : matieres) {
System.out.println(mat);
}*/
This problem has nothing to do with Hibernate. Please inspect the stack trace carefully: your Enseigner.toString() calls Professeur.toString() which in turn calls Enseigner.toString() again and so on.
I notice this problem more and more these days when people blindly use Lombok with its #Data (which should almost never be used), #ToString and #EqualsAndHashCode. These generate respective methods that include all fields!
You need to remove these annotations or set them up so that they use only the fields that you really need. Most of the time your equals() and hashCode() are not needed when you write web apps with ORM. Hibernate ensures you don't have 2 instances of the same entity.
On the other hand toString() can be useful, but we shouldn't include all fields in it - just the ones that are helpful in identifying the entity.
You have cyclic reference. You need exclude field professeurs and matieres by #JsonIgnoreProperties
Related
I have a Java User class, a user can have friends (List<User>). By default, Hibernate create two tables : USER and USER_FRIENDS(USER_ID,FRIENDS_ID)
The problem is when I change friends in my code and that I save(user), spring add the new friends but don't remove in the database the friends removed from the array list.
#Entitypublic class User implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String pseudo;
private String password;
private String email;
private Gender gender;
#Lob
private byte[] avatar;
private String description;
private Date birthdate;
#ManyToMany(cascade = CascadeType.ALL)
private List<Game> favoriteGames = new ArrayList<>();
#OneToMany( cascade = CascadeType.ALL)
private List<User> friends = new ArrayList<>();
I tried #ManyToMany, #OneToMany, cascade = CascadeType.ALL
Basically, first I would advise that you take special care with your equals and hashCode implementation in your entities. You did not show us that, but it should be something like this in your User.java:
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User other = (User) o;
return id != null && id.equals(other.getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
Those are very important, especially when working with entities in collections.
Secondly, a connection between a User and his Friends (other Users) should be modeled as Many-to-Many, because:
every user can be a friend to MANY of other users
every user can have any number of friends, in other words MANY friends
And I would model this connection like this:
#ManyToMany
#JoinTable(name = "user_friends", joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "friend_user_id"))
private Set<User> friends = new HashSet<>();
I am trying to have a bi-directional relationship between the child(ZonePoint_DTO) and parent(ZoneDTO), where the parent has a composite key.
The current mapping is resulting in nulls being entered in the child(ZonePoint_DTO) table when I try to enter a whole parent object(ZoneDTO) with a list of child(ZonePoint_DTO) entities. Please note the I have set cascade type to Cascade.PERSIST.
I am using JpaRepository.save() to save Parent(ZoneDTO)
Below are the entity classes.
#Entity
#Getter
#Setter
public class ZoneDTO implements GenericPJM<ZoneDTO_Id>, Persistable<ZoneDTO_Id> {
private static Set<String> zoneNamesList;
#EmbeddedId private ZoneDTO_Id id;
private Integer noOfPolygons;
#OneToMany(mappedBy = "zoneDTO", fetch = FetchType.EAGER)
#Cascade(value = org.hibernate.annotations.CascadeType.PERSIST)
private List<ZonePointDTO> zonePointsList;
#Override
public boolean isNew() {
boolean result = !zoneNamesList.contains(id.getZoneName());
System.out.println(
"__________________________isNew() called for " + this.getClass().getSimpleName() + "result= " + result);
return result;
}
public static void setZoneNamesList(Set<String> zoneNamesListNew) {
zoneNamesList = zoneNamesListNew;
}
}
#Getter
#Setter
#Embeddable
public class ZoneDTO_Id implements GenericPJM_Id {
String zoneName;
}
#Entity
#Getter
#Setter
public class ZonePointDTO implements GenericPJM<Long> {
#JsonIgnore #Id #GeneratedValue private Long id;
private double xValue;
private double yValue;
private Integer polygonNumber;
#ManyToOne private ZoneDTO zoneDTO;
}
The JSON format I am sending:
{
"type":"ZoneDTO",
"id": {"zoneName":"SAMPLE"},
"noOfPolygons": 1,
"zonePointsList": [
{
"type":"ZonePointDTO",
"xValue": 10,
"yValue": 39.5,
"polygonNumber": 1
},
{
"type":"ZonePointDTO",
"xValue": 10,
"yValue": 39,
"polygonNumber": 1
}
]
}
I want to cascade persist the parent object(ZoneDTO), all at once and hopefully minimal DB calls.
Currently, data is getting persisted, both parent and child. But the child mapping column is saving nulls.
Any comments or workarounds or other methods will be helpful.
Thanks in advance.
I am new to Spring Data JPA and CRUD repositories. I have code that can save to the database if I save each individual entity, but thought that I should be able to save the parent entity and have the contained child entities automatically get saved or updated. Am I doing something wrong?
The code that executes the save:
private static CustomerRepository customerRepository;
#Transactional
public ResultCreateCustomer addCustomer(NameTable nameIn, Address addressIn)
throws Exception {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
customerRepository = context.getBean(CustomerRepository.class);
ResultCreateCustomer result = new ResultCreateCustomer(0, 0, 0, DATABASE_OR_SYSTEM_ERROR);
try {
NameTable theName = new NameTable();
theName.setFirstName(nameIn.getFirstName());
theName.setLastName(nameIn.getLastName());
Address theAddress = new Address();
theAddress.setStreetNo(addressIn.getStreetNo());
theAddress.setStreetName(addressIn.getStreetName());
theAddress.setCityStateZip(addressIn.getCityStateZip());
Customer theCustomer = new Customer();
theCustomer.setNameTable(theName);
theCustomer.setAddress(theAddress);
customerRepository.save(theCustomer);
} catch (Exception e) {
this.log.error("got exception: " + e.getClass() + ": " + e.getMessage());
}
context.close();
return result;
}
The code for the Customer entity:
#Entity
#org.hibernate.annotations.Proxy(lazy=false)
#Table(name="Customer")
public class Customer implements Serializable {
public Customer() {
}
#Column(name="ID", nullable=false, unique=true)
#Id
#GeneratedValue(generator="COM_COMPORIUM_CUSTOMER_DOMAIN_CUSTOMER_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name="COM_COMPORIUM_CUSTOMER_DOMAIN_CUSTOMER_ID_GENERATOR", strategy="native")
private int ID;
#ManyToOne(targetEntity=com.comporium.customer.addresses.Address.class, fetch=FetchType.LAZY)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="AddressID", referencedColumnName="ID") })
private com.comporium.customer.addresses.Address address;
#OneToOne(targetEntity=com.comporium.customer.contactrolodex.NameTable.class, fetch=FetchType.LAZY)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="NameTableID", nullable=false) })
private com.comporium.customer.contactrolodex.NameTable nameTable;
#Column(name="IndustryCode", nullable=false)
private int industryCode;
#Column(name="DemographicCode", nullable=false)
private int demographicCode;
#Column(name="Ranking", nullable=false)
private int ranking;
public void setNameTable(com.comporium.customer.contactrolodex.NameTable value) {
this.nameTable = value;
}
public com.comporium.customer.contactrolodex.NameTable getNameTable() {
return nameTable;
}
// Other setters and getters follow
}
The code for the first child class:
#Entity
#org.hibernate.annotations.Proxy(lazy=false)
#Table(name="NameTable")
public class NameTable implements Serializable {
public NameTable() {
}
#Column(name="ID", nullable=false, unique=true)
#Id
#GeneratedValue(generator="COM_COMPORIUM_CUSTOMER_CONTACTROLODEX_NAMETABLE_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name="COM_COMPORIUM_CUSTOMER_CONTACTROLODEX_NAMETABLE_ID_GENERATOR", strategy="native")
private int ID;
#Column(name="FirstName", nullable=true, length=30)
private String firstName;
#Column(name="LastName", nullable=true, length=40)
private String lastName;
#OneToOne(mappedBy="nameTable", targetEntity=com.comporium.customer.domain.Customer.class, fetch=FetchType.LAZY)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
private com.comporium.customer.domain.Customer customer;
}
The repository interface, CustomerRepository:
package com.comporium.customer.repositories;
import org.springframework.data.repository.CrudRepository;
import com.comporium.customer.domain.Customer;
public interface CustomerRepository extends CrudRepository<Customer, Integer> {
}
When I run the executable (via a SOAP call), I get an exception:
2014-11-19 09:57:59,253 ERROR VPcodeDao:306 - got exception: class org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.comporium.customer.domain.Customer.nameTable -> com.comporium.customer.contactrolodex.NameTable; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.comporium.customer.domain.Customer.nameTable -> com.comporium.customer.contactrolodex.NameTable
Is there a way for the save(theCustomer) to save the contained child entities also?
not sure if you ever get a solution for this. but the solution is to define cascading
#OneToOne(cascade = {CascadeType.ALL}, mappedBy="nameTable", targetEntity=com.comporium.customer.domain.Customer.class, fetch=FetchType.LAZY)
I have two entities: Account, Home. The relationship is such
#Entity
public class Account implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key userId;
private String data;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Home> homes = new ArrayList<Home>();
public Account() {
}
public Account(String data) {
this. data = data;
}
public Account(String data, List<Home> homes) {
super();
this. data = data;
this.homes = homes;
}
public List<Home> getHomes() {
return homes;
}
public void setHomes(List<Home> homes) {
this.homes = homes;
}
}
//HOME
#Entity
public class Home implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private long timestamp;
private long userId;
public Home(long latlong, long userId) {
Key key = KeyFactory.createKey(Home.class.getSimpleName(), latlong + "" + userId);
this.key = key;
this.userId = userId;
}
//getters and setters
}
The invariants are such that the latlong for a user never changes. Therefore, when I add a Home entity, if one already exists for said user, it is simply replaced in the datastore. So far so good. But I am getting a problem for the following user case:
Say user X edit his home data 6 times. Sure enough when I look in the datastore, there is only one entry for Home and it contains the latest data. But after querying for Account using datanucleus, when I do getHomes().size() the result is 6. Apparently datanucleus is caching every single edit as an individual entity. It’s a mystery to me. I am logging the code so I am seeing it happening: For the exact same key (I use a for-loop), I am getting each iteration/edit of the entity. How do I keep this from happening? The datastore clearly shows one entity for the key. I just want that one entity: no historical list.
My datanucleus/jpa getById is
#Override
public T getById(Long id) {
EntityManager mgr = getEntityManager();
try {
return null == id ? null : mgr.find(type, id);
} finally {
mgr.close();
}
}
Still baffled per the root cause of the problem, I replaced List with Set and my problem is now solved.
I have a Priv class
#Entity
#Table(name = "PK_PRIVS", schema = "dbo")
public class Priv implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private String code;
private String name;
private String description;
private PrivType type;
//...
}
and a Report class which has many to many relation with Priv and contains Set of associated Privs - privs.
#Entity
#Table(name = "REPORT", schema = "dbo")
public class Report implements java.io.Serializable {
//...
private Set<Priv> privs = new HashSet<Priv>(0);
//...
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "REPORT_PK_PRIVS", schema = "dbo", joinColumns = { #JoinColumn(name = "REPORT_ID") }, inverseJoinColumns = { #JoinColumn(name = "PK_PRIVS_CODE") })
public Set<Priv> getPrivs() {
return this.privs;
}
public void setPrivs(Set<Priv> privs) {
this.privs = privs;
}
}
Now I have a Set of Strings, which are codes of Priv classes (code is Primary Key in Priv).
Set<String> privsCodesSet; //set of codes of Priv classes
I need a criterion which allow me to find that Reports, which all codes from its Priv set contains in privsCodesSet. For example if I have privsCodeSet = {"code1", "code2"}
Report with privs with codes {"code1"" should be in result, but
Report with privs with codes {"code1", "code2", "code3"} should not.
I also have class which is join of Priv and Report, but I'm not sure if it's help.
This code should work
Criteria reportPrivCriteria = currentSession()
.createCriteria( Report.class, "r");
reportPrivCriteria.createAlias("privs", "p");
reportPrivCriteria.add(Restrictions.in(p.code, privsCodeSet));
Do you have something like this on your Priv class?
private Set<Report> reports;
// ...
#ManyToMany(mappedBy="privs")
public Collection<Report> getReports() {
return reports;
}