I am trying to use Hibernate for the first time, so I am not very familiar with the syntax yet. I am trying to model a library system. The classes that are causing problem are "Borrow" and "CopyBook" which are as following:
import javax.persistence.*;
#Entity
#Table(name = "copybook", schema="project")
public class CopyBook {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="copy_id", unique = true, nullable = false)
private int copyId;
#Convert(converter = StatusAttributeConverter.class)
#Column(name="status", columnDefinition = "TEXT DEFAULT 'AVAILABLE'")
private Status status;
#ManyToOne(optional = false)
#JoinColumn(name = "book_id")
private Book book;
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
#OneToOne(mappedBy = "copy")
private Borrow borrow;
public Borrow getBorrow() {
return borrow;
}
public void setBorrow(Borrow borrow) {
this.borrow = borrow;
}
#OneToOne(mappedBy = "copy", optional = false)
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "borrow", schema = "project")
public class Borrow {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="no_borrow", unique = true, nullable = false)
private int noBorrow;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="date_borrow", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = false, nullable = false)
private Date dateBorrow;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="date_return")
private Date dateReturn;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="max_date_return", insertable = false, updatable = false)
private Date maxDateReturn;
#OneToOne(optional = false)
#JoinColumn(name = "copy_id")
#NotFound(action = NotFoundAction.IGNORE)
private CopyBook copy;
public CopyBook getCopy() {
return copy;
}
public void setCopy(CopyBook copy) {
this.copy = copy;
}
#ManyToOne(optional = false)
#JoinColumn(name="mem_id")
private Members member;
public Members getMember() {
return member;
}
public void setMember(Members member) {
this.member = member;
}
}
I am using IntelliJ, so when I try to run the query of the Borrow entity it showed me that it cannot find CopyBook with id 1 even though it actually exists in the database
This is the entries currently present in my database (In these two table)
CopyBook:
Borrow:
As we can see, there is indeed a CopyBook with id of 1. This is also proved when I run the query of CopyBook entity which successfully returned all the results
Result after running in JPA select entity from CopyBook entity
Just to see what results Borrow is actually giving, I have added #NotFound(action = NotFoundAction.IGNORE). And this is the result I get
So I found this is a very bizarre situation because all data exist, and CopyBook is able to find its correspondent Borrow. But Borrow is unable to find its correspondent CopyBook ?
Strangley, I have another entity class Order which has almost the same attributes (Also has a OneToOne relationship with CopyBook), and it works perfectly.
Problem Solved.
Because I have multiple One-To-Many relationship, and the FetchType is by default Eager. Whenever I want to retrieve the entity, it will perform Left Outer Join for all the entities until the end.
However, under the scope of this context. A book does not have to be ordered to exist in the library, be performing "JOIN" it loses entries and therefore causes the aforementioned problem.
As a result, all I did was to set the attributesOrder and Borrow with optional = true and fetch = Fetchtype.Lazy so it will not perform join unless necessary
Related
I am using Spring Data JPA with MS SQL. I have a table named timeseries.
Timeseries.java:
#Entity(name = "timeseries")
#Table(indexes = { #Index(columnList = "agent_id, aspect_id, _time") })
#IdClass(TimeseriesId.class)
public class Timeseries
{
#Id
#Column(name = "agent_id", nullable = false)
private String agentId;
#Id
#Column(name = "aspect_id", nullable = false)
private String aspectId;
#Id
#Column(name = "_time", nullable = false)
private Timestamp time;
#Column(name = "value", nullable = false, columnDefinition = "TEXT")
private String value;
// needed for hibernate
public Timeseries()
{
}
public Timeseries(Timestamp time, String agentId, String aspectId, String value)
{
super();
this.time = time;
this.agentId = agentId;
this.aspectId = aspectId;
this.value = value;
}
}
I did not want to keep exclusive column only for id. So I wanted to build a composite primary key.
My key will be composed of agent_id, aspect_id and _time
So here is my Id class:
public class TimeseriesId implements Serializable
{
private String agentId;
private String aspectId;
private Timestamp time;
public TimeseriesId()
{
}
public TimeseriesId(String agentId, String aspectId, Timestamp time)
{
this.agentId = agentId;
this.aspectId = aspectId;
this.time = time;
}
//... hashCode and equals methods
}
I thought giving 3 columns as primary key would make my read queries faster but now I think JPA is not using my primary key. Because it is resulting much more slower response times. Here is my repository interface.
public interface TimeseriesRepository extends JpaRepository<Timeseries, TimeseriesId>
{
List<Timeseries> findByAgentIdAndAspectIdAndTimeBetween(String agentId, String aspectId, Timestamp from,
Timestamp to);
Optional<Timeseries> findById(TimeseriesId id);
}
Here how I call in my service layer:
TimeseriesId id = new TimeseriesId(agentId, aspectId, timeStamp);
Optional<Timeseries> timeseriesOptional = timeseriesRepository.findById(id);
//... or the other one as:
List<Timeseries> timeSeriesList = timeseriesRepository.findByAgentIdAndAspectIdAndTimeBetween(agentId,
aspectId, timeStampFrom, timeStampTo);
But my problem is my query findByAgentIdAndAspectIdAndTimeBetween is not using the index. How to enforce my indexes are used in the query. I am also not sure if findById also uses an index or not. Can you please help me to ensure and enforce the usage of the indexes?
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
I recently upgraded from hibernate-core 4.1.7 to 5.0.9 and Have problem with this code:
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
#JoinColumn(name = "FK_AAA", foreignKey = #ForeignKey(name = "CS_BBB"))
#org.hibernate.annotations.Index(name = "IDX_CCC", columnNames = "FK_DDD")
private ImportData importData;
This generate correct foreign columns pointing to the defining class, but also generating a column on the same class:
IMPORTDATA RAW(255)
Why is this raw(255) column generated ? I think it was not generated with Hibernate-core 4.1.7
any idea ?
Update 1: here is longer code fragments:
#MappedSuperclass
#Access(AccessType.PROPERTY)
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public abstract Long getId();
}
#Entity
#Table(name = "IMPORT_DATA", uniqueConstraints = {
#UniqueConstraint(name = "UC_IMP_BID", columnNames = {"BUSINESS_ID"})
}, indexes = {
#Index(name = "IDX_IMP_DGXML_ID", columnList = "FK_DGXML_ID"),
#Index(name = "IDX_IMP_IMPXML_ID", columnList = "FK_IMPXML_ID")
})
public class ImportData extends BaseEntity {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() { return id; }
// ...
}
#Entity(name = "MUTATION")
#Table(name = "MUTATION")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
#SequenceGenerator(name = "mutationsSeq", sequenceName = "MUTATIONS_SEQUENCE", allocationSize = 1)
public abstract class Mutation extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "mutationsSeq")
private Long id;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
#JoinColumn(name = "FK_IMP_ID", foreignKey = #ForeignKey(name = "CS_MUT_IMP_ID"))
#org.hibernate.annotations.Index(name = "IDX_MUT_IMP_ID", columnNames = "FK_IMP_ID")
protected ImportData importData;
}
#Entity(name="XXX")
#DiscriminatorValue("XXX_DISC")
public class XXX extends Mutation {
// ...
}
I found an answer on Mapping composite key with Hibernate produces a raw field in Oracle:
I was mixing annotations on fields and methods. I also had #Id on an abstract superclass, and a redefinition on a derived class.
Fixing theses two elements, cleaning DB and regenerating in "create" ddl mode proved that the fix was no longer generating RAW field type.
Thanks for all your helps!
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 two entities: Dealers and Makes. Dealers have multiple makes.I am able to insert the records as a one-to-many relationship between these two entities.
However, I am not able to retrieve the records based on dealer ID. I tried many different ways and it's still throwing this exception: Property not found make(it is table) of : class name(full classname)
If anyone could provide me with some hints, it would be greatly appreciated.
Dealer:
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.OneToMany;
import javax.persistence.JoinTable;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;
#Entity
#Table(name = "dealer")
public class Dealer implements java.io.Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
public Dealer(){
}
public Dealer(String DealerName,String dealerPhno,Set makes){
this.dealerName=DealerName;
this.dealerphno=dealerPhno;
this.dealerMakes=makes;
}
public void setDealerMakes(Set<Make> dealerMakes) {
this.dealerMakes = dealerMakes;
}
#Id
#GeneratedValue
#Column(name = "dealer_id")
private long dealerId;
public long getDealerId() {
return dealerId;
}
public void setDealerId(long dealerId) {
this.dealerId = dealerId;
}
public String getDealerName() {
return dealerName;
}
public void setDealerName(String dealerName) {
this.dealerName = dealerName;
}
public String getDealerphno() {
return dealerphno;
}
public void setDealerphno(String dealerphno) {
this.dealerphno = dealerphno;
}
#Column(name = "dealer_name" ,unique=true, nullable = false, length=20)
private String dealerName;
#Column(name = "dealer_phno" , nullable = false, length=20)
private String dealerphno;
#OneToMany(cascade = CascadeType.ALL)
//#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name="dealer_id", nullable=false)
//#JoinTable(name = "dealer_make", joinColumns = { #JoinColumn(name = "dealer_id") }, inverseJoinColumns = { #JoinColumn(name = "make_id") })
private Set<Make> dealerMakes = new HashSet<Make>(0);
public Set<Make> getDealerMakes() {
return dealerMakes;
}
}
Make
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Entity;
import javax.persistence.Table;
#Entity
#Table(name = "make")
public class Make implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 2L;
public Make(){
}
public Make(String makeName,String modelName,String price){
this.makeName=makeName;
this.modelName=modelName;
this.price=price;
}
public long getMakeId() {
return makeId;
}
public void setMakeId(long makeId) {
this.makeId = makeId;
}
public String getMakeName() {
return makeName;
}
public void setMakeName(String makeName) {
this.makeName = makeName;
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
#Id
#GeneratedValue
#Column(name = "make_id")
private long makeId;
#Column(name = "make_name" , nullable = false, length=8)
private String makeName;
#Column(name = "model_name" , nullable = false, length=8)
private String modelName;
#Column(name = "price" , nullable = false, length=8)
private String price;
}
Query I tried:
Criteria dealer=sessionFactory.getCurrentSession().createCriteria(Dealer.class);
Criteria make=dealer.createCriteria(make);
make.add(Restrictions.gt("dealerId",new Long(dealerId)));
List<Dealer> results=dealer.list(); //throwing exception
Ouput i am expecting:
Dealer_Id dealername phonenumber
1 halal 074563485
Make has below records:
make_id makename makeprice dealer_id
1 ford 3000$ 1
2 hyundai 2000$ 1
I want results like dealer details with make details.
OK. So you want to load dealers with their makes, and you only want the dealers having an ID greater than a given ID.
First of all, you could just load dealers. The list of their makes would automatically be loaded by Hibernate when you would call getDealerMakes():
Criteria c = session.createCriteria(Dealer.class);
c.add(Restrictions.gt("dealerId", dealerId)); // let's say delerId = 6
List<Dealer> dealers = c.list(); // execute SQL query select d.* from dealer d where d.dealer_id > 6
for (Dealer dealer : dealers) {
Set<Make> make = dealer.getDealerMakes() // execute SQL query select m.* from make m where m.dealer_id = <theIdOfTheCurrentDealer>
}
This has the disadvantage of executing N + 1 queries.
So if you want to load the dealers and their makes in a single query, you need to set a fetch mode so that the makes are loaded. Note that since the join uses the association that you defined between Dealer and Make, you don't need to tell Hibernate that the ID of the dealer table must be equal to the dealer_id of the make table: Hibernate already knows that thanks to the JoinColumn annotation that you put on the association. The query is thus the following:
Criteria c = session.createCriteria(Dealer.class);
c.add(Restrictions.gt("dealerId", dealerId));
// this tells Hibernate that the makes must be fetched from the database
// you must use the name of the annotated field in the Java class: dealerMakes
c.setFetchMode("dealerMakes", FetchMode.JOIN);
// Hibernate will return instances of Dealer, but it will return the same instance several times
// once per make the dealer has. To avoid this, you must use a distinct root entity transformer
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List<Delaer> dealers = c.list(); // executes the SQL query:
// select d.*, m.* from dealer d left join make m on d.dealer_id = m.dealer_id where d.dealer_id > 6
for (Dealer dealer : dealers) {
Set<Make> make = dealer.getDealerMakes() // no SQL query. The makes are already loaded
}
criteria.setFetchMode("dealerMakes", FetchMode.JOIN);
criteria.createCriteria("dealerMakes");
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
This do the trick!
public List<Dealer> findDealers() {
Criteria dealer=sessionFactory.getCurrentSession().createCriteria(Dealer.class);
Criteria make=dealer.createCriteria(make);
make.setFetchMode("dealer", FetchMode.JOIN);
make.add(Restrictions.gt("dealerId",new Long(dealerId)));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); // depends on wat u want
return criteria.list();
}
try to return like this, hope it works, let me know if there is more to it