Mapping #ManyToOne / Foreign Key Hibernate Java - java

First of all, sorry if the subject has already been answered, but i've been searching for almost 6hours in a row, and trying everything i could find on the web, but i'm still stuck..
I Have a problem when i'm executing my code.. I can't find the origin of it, nor a solution...
Here's my different classes: (btw it's my first post on SOF, let me know if you need more information).
I'm going to post my Pojo's, useful DAO's and MySQL table creation orders, and the error message i get.
POJOs:
-Celebrite:
#Entity
#Table(name="Celebrite")
public class Celebrite implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name="numCelebrite")
#GeneratedValue(strategy=GenerationType.IDENTITY)
#JoinColumn(name="numCelebrite")
private int numCelebrite;
#Column(name="nom")
private String nom;
#Column(name="prenom")
private String prenom;
#Column(name="nationalite")
private String nationalite;
#Column(name="epoque")
private String epoque;
public Celebrite() {
super();
}
public Celebrite(String nom, String prenom, String nationalite, String epoque) {
super();
this.nom = nom;
this.prenom = prenom;
this.nationalite = nationalite;
this.epoque = epoque;
}
public int getNumCelebrite() {
return numCelebrite;
}
public void setNumCelebrite(int numCelebrite) {
this.numCelebrite = numCelebrite;
}
//Other getters/setters
}
-Monument
#Entity
public class Monument implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private String codeM;
private String nomM;
private String proprietaire;
private String typeMonument;
private float latitude;
private float longitude;
public Monument() {
super();
}
public Monument( String codeM,String nomM, String propritaire, String typeMonument, float latitude, float longitude) {
super();
this.codeM=codeM;
this.nomM = nomM;
this.proprietaire = propritaire;
this.typeMonument = typeMonument;
this.latitude = latitude;
this.longitude = longitude;
}
public Monument( String nomM, String propritaire, String typeMonument, float latitude, float longitude) {
super();
this.nomM = nomM;
this.proprietaire = propritaire;
this.typeMonument = typeMonument;
this.latitude = latitude;
this.longitude = longitude;
}
public String getCodeM() {
return codeM;
}
public void setCodeM(String codeM) {
this.codeM = codeM;
}
//other getters/setters..
}
-AssocieA (translation : AssociatedTo)
#Entity
public class AssocieA implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#ManyToOne(fetch=FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="numCelebrite",referencedColumnName="numCelebrite")
private Celebrite celebrite;
//private int numCelebrite;
#Id
#ManyToOne(fetch=FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="codeM",referencedColumnName="codeM")
//private String codeM;
private Monument monument;
public AssocieA() {
}
public AssocieA(Celebrite celebrite, Monument monument) {
super();
this.celebrite = celebrite;
this.monument = monument;
}
//getters/setters
}
Ok now the DAO, i'm only posting AssocieA's DAO as the others are working perfectly
public class DAOAssocieA {
EntityManagerFactory emf;
EntityManager em ;
public DAOAssocieA() {
super();
this.emf = Persistence.createEntityManagerFactory("jpa-societe-pu");
this.em = emf.createEntityManager();
}
public List<AssocieA> getAssociatedMonuments(int numCelebrite){
Query req=em.createQuery("Select a from AssocieA a where a.numCelebrite =" + numCelebrite);
return req.getResultList();
}
public List<AssocieA> getAssociatedCelebrities(String codeM){
Query req=em.createQuery("Select a from AssocieA a where a.codeM = '"+codeM+"'");
return req.getResultList();
}
}
Finally, the 'main' class
public String execute() {
setDAOc(new DAOCelebrite());
setDAOm(new DAOMonument());
setDAOa(new DAOAssocieA());
if (getNom()==null)
setNom("");
if (getPrenom() == null)
setPrenom("");
if (getNationalite() == null)
setNationalite("");
if (getEpoque()==null)
setEpoque("");
setListeCelebrite(DAOc.getCelebritiesBy(getNom(),getPrenom(),getNationalite(), getEpoque()));
System.out.println(getAssociated());
if (getAssociated().equals("on")) {
for (Celebrite c:listeCelebrite) {
for (AssocieA a : DAOa.getAssociatedMonuments(c.getNumCelebrite())){
System.out.println(a.getCelebrite());
System.out.println(a.getMonument());
}
}
}
return ("success");
-> The return ("success") is because i use Struts2
Now, Table creation orders (on MySQL)
CREATE TABLE Celebrite (numCelebrite int auto_increment, nom varchar(16), prenom varchar(16), nationalite varchar(10), epoque varchar(6), PRIMARY KEY (numCelebrite)) ENGINE=InnoDB;
CREATE TABLE Monument (codeM varchar(5), nomM varchar(25), proprietaire varchar(10), typeMonument varchar(16), longitude float, latitude float, PRIMARY KEY (codeM)) ENGINE=InnoDB;
CREATE TABLE AssocieA (codeM varchar(5), numCelebrite int, PRIMARY KEY (codeM,numCelebrite), FOREIGN KEY (codeM) REFERENCES Monument(codeM), FOREIGN KEY (numCelebrite) REFERENCES Celebrite(numCelebrite)) ENGINE=InnoDB;
To finish, the error message i get:
org.hibernate.QueryException: could not resolve property: numCelebrite of: pojo.AssocieA [Select a from pojo.AssocieA a where a.numCelebrite =1]
I understand that the class 'AssocieA' doesn't have a "numCelebrite" property, but i thought that because of the #ManyToOne annotation, Celebrite Table should be loaded when Associe is loaded.
Else, could you give me some tips to explain how to do that?
The final goal is: having a Celebrite, i'd like, using the numCelebrite, to retrieve every Monuments related to it, using the AssocieA table.
Thank you in advance
EDIT: Solution found on another website by kulturman:
I was using native queries:
em.createQuery("from AssocieA a where a.numCelebrite  =" + numCelebrite);
Instead of JPQL (HQL queries):
em.createQuery("from AssocieA a where a.celebrite.numCelebrite  =" + numCelebrite);
For those who want to see directly the solution, it's in french on OpenClassRoom

Try removing the #JoinColumn(name="numCelebrite") and #GeneratedValue(strategy=GenerationType.IDENTITY) in celebrity pojo, change CascadeType.ALL to CascadeType.Persist and let us know what happen. I'm not an expert but I think that way you can do the trick.

Solution found on another website by kulturman:
I had to replace:
em.createQuery("from AssocieA a where a.numCelebrite =" + numCelebrite);
Instead of JPQL (HQL queries):
by:
em.createQuery("from AssocieA a where a.celebrite.numCelebrite =" + numCelebrite);
Here's the explanation:
I was building my query as if i was requesting my database :
The table AssocieA has the attribute "numCelebrite"
But in my case, I mapped the tables to my classes: Each row of the AssocieA table is now an instance of my AssocieA class.
Using a HQL query, i'm requesting on the instance of my class, not the table of my database.
So what i have to do is a.getCelebrite().getNumCelebrite(), hence the "a.celebrite.numCelebrite"
I hope i've made myself clear enough because my english is not that good.
Here's the link of the forum i had this answer on (it's in french):
https://openclassrooms.com/forum/sujet/org-hibernate-queryexception?page=1#message-92165885

Related

How do I get the name of the audited table in RevisionInfo using Envers?

Intending to get the name of the table that was audited. Hopefully maybe by taking the object which is sent to revision info before it writes. Is there a way to do that.
#Entity
#RevisionEntity
#Table(name = "revision_info")
public class revision_info{
#Column(name = "tableName", length=36)
private String tableName=getTableName();
public revision_info() {
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}

REST web service returns a field that does not exist in the underlying SQL query

When I try to access the following REST service, it returns all data plus a field that does not exist in the "SupplierPayment" entity class "customerId".
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<SupplierPayment> getAllSupplierPaymentsService() {
return (ArrayList<SupplierPayment>) supplierPaymentDao.getAllSupplierPayments();
}
Here is the getAllSupplierPayments() method:
public List<SupplierPayment> getAllSupplierPayments() {
String query = "SELECT * FROM supplierpayment";
return (ArrayList<SupplierPayment>) getJdbcTemplate().query(query,
new BeanPropertyRowMapper<SupplierPayment>(SupplierPayment.class));
}
Here are all the fields in the "SupplierPayment" class:
private Integer supplierPaymentId;
private BigDecimal amount;
private Integer purchaseInvoiceId;
private Integer supplierId;
private Integer paymentMethodId;
private String description;
private Integer checkId;
private Integer fromBankAccountId;
private Integer toBankAccountId;
private String creditCardNo;
private Timestamp created;
private Integer createdBy;
When I debug, I find that the ArrayList of the web service does not return that field, then somehow I find that field in the response.
I tried truncating and dropping the table and adding the columns one after the other. What I found is the field is returned in the response only when I add the column "supplierId" to the table and its value is the same of "supplierId". I think the column "customerId" existed before and I dropped it.
I found that the following getter and setter existed in the SupplierPayment entity class because of the previously existing column in the table:
public Integer getCustomerId() {
return supplierId;
}
public void setCustomerId(Integer customerId) {
this.supplierId = customerId;
}
By removing them, the problem was solved.

JPA or Hibernate to generate a (non primary key) column value, not starting from 1

I want a JPA/Hibernate (preferably JPA) annotation that can generate the value of a column, that is not a primary key and it doesn't start from 1.
From what I have seen JPA cannot do that with #GeneratedValue and #SequenceGenerator and #TableGenerator. Or with anything else.
I have seen a solution with an extra table, which I find is not elegant.
I can live with a Hibernate annotation, because I already have hibernate annotations.
I want to use #Generated but I cannot make it work and people claim that it is possible.
#Generated(GenerationTime.INSERT)
private long invoiceNumber;//invoice number
Update: an extra requirement, if the transaction is rolled back, we can't have a gap in the numbering.
Anyone?
The #GeneratedValue only works for identifiers and so you can't use it. If you use MySQL, you are quite limited, since database sequences are not supported.
InnoDB doesn't support multiple AUTO_INCREMENT columns and if your table PK is AUTO_INCREMENTED, then you have two options:
Go for a separate table that behaves like a sequence generator, the solution you already said you are not happy about.
Use an INSERT TRIGGER to increment that column.
Here's what worked for me - we coded all of it in the service.
Here's the entity:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Registrant extends AbstractEntity {
//....
private long invoiceNumber;//invoice number
#Entity
public static class InvoiceNumberGenerator {
#Id
#GeneratedValue
private int id;
private long counter;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getCounter() {
return counter;
}
public void setCounter(long counter) {
this.counter = counter;
}
}
}
And then we have a service that does the magic (actually there's no magic, all is done manually):
public synchronized Registrant save(Registrant registrant) {
long counter = getInvoiceNumber();
registrant.setInvoiceNumber(counter);
return registrantRepository.save(registrant);
}
private long getInvoiceNumber() {
//mist: get the invoice number from the other table
long count = registrantInvoiceNumberGeneratorRepository.count();
if(count > 1) {
throw new RuntimeException(": InvoiceNumberGenerator table has more than one row. Fix that");
}
Registrant.InvoiceNumberGenerator generator;
if(count == 0) {
generator = new Registrant.InvoiceNumberGenerator();
generator.setCounter(1000001);
generator = registrantInvoiceNumberGeneratorRepository.save(generator);
} else {
generator = registrantInvoiceNumberGeneratorRepository.findFirstByOrderByIdAsc();
}
long counter = generator.getCounter();
generator.setCounter(counter+1);
registrantInvoiceNumberGeneratorRepository.save(generator);
return counter;
}
Note the synchronized method - so that nobody can get the same number.
I can't believe there's nothing automatic that can do that.
Related to #Vlad Mihalcea, now you can use #GeneratorType to generate your own custom value for non id column. For example:
Entity:
import org.hibernate.annotations.GeneratorType
#GeneratorType(type = CustomGenerator.class, when = GenerationTime.INSERT)
#Column(name = "CUSTOM_COLUMN", unique = true, nullable = false, updatable = false, lenght = 64)
private String custom;
ValueGenerator implementation:
public class CustomGenerator extends ValueGenerator<String> {
private static final String TODAY_EXAMPLE_QUERY = "from Example where createDate>:start and createDate<:end order by createDate desc";
private static final String START_PARAMETER = "start";
private static final String END_PARAMETER = "end";
private static final String NEXTVAL_QUERY = "select EXAMPLE_SEQ.nextval from dual";
private final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd");
#Override
public String generateValue(Session session, Object owner) {
Date now = new Date();
Query<Example> todayQuery = session.createQuery(TODAY_EXAMPLE_QUERY, Example.class);
query.setParameter(START_PARAMETER, start(now));
query.setParameter(END_PARAMETER, end(now));
Example lastExample = todayQuery.setMaxResult(1).setHibernateFlushMode(COMMIT).uniqueResult();
NativeQuery nextvalQuery = session.createSQLQuery(NEXTVAL_QUERY);
Number nextvalValue = nextvalQuery.setFlushMode(COMMIT).uniqueResult();
return dataFormat.format(now) + someParameter(lastExample) + nextvalValue.longValue();
}
}

Using #IdClass for storing entity with composite primary key, but fails to persist

my id class as follows,
public class EmployeeId implements Serializable{
public EmployeeId(){}
public EmployeeId(Integer id, String country){
this.id = id;
this.country = country;
}
private Integer id;
private String country;
#Override
public int hashCode(){
return this.getCountry().hashCode() + getId();
}
#Override
public boolean equals(Object o){
boolean flag = false;
EmployeeId myId = (EmployeeId) o;
if((o instanceof EmployeeId)
&& (this.getCountry().equals(myId.getCountry()))
&& (this.id == myId.getId())){
flag = true;
}
return flag;
}
// rest of the code with getters only
}
Following is my entity using
#Entity
#IdClass(EmployeeId.class)
#Table(name="TBL_EMPLOYEE_FOUR")
public class EmployeeEntityTwo {
public EmployeeEntityTwo(){}
public EmployeeEntityTwo(Integer id,String country, String empName){
this.country = country;
this.employeeId = id;
this.empName = empName;
}
#Id
#Column(name="ID")
private Integer employeeId;
#Id
#Column(name="COUNTRY",length=50)
private String country;
#Column(name="NAME",length=50)
private String empName;
// getters and setters
}
This is my table
create table TBL_EMPLOYEE_FOUR(
ID integer,
COUNTRY varchar(50),
NAME varchar(50),
constraint PK_EMP_00239 primary key(ID,COUNTRY)
)
This is what i am trying to run
private static void idClassStore(EntityManager em) throws Exception{
List<EmployeeEntityTwo> employees = Arrays.asList(new EmployeeEntityTwo(12, "KENYA", "Ridushi Ogambe"),
new EmployeeEntityTwo(13, "GHANA", "Mikila Hanza"),
new EmployeeEntityTwo(14, "EGYPT", "Abdul Hameed Fagdaul"),
new EmployeeEntityTwo(15, "MOROCCO", "Jamil Mahmoud"),
new EmployeeEntityTwo(16, "LIBERIA", "Robert Damus"));
for(EmployeeEntityTwo employee : employees){
em.persist(employee);
}
}
But i get an exception as
Caused by: org.hibernate.AnnotationException: Property of #IdClass not found in entity com.entities.EmployeeEntityTwo: id
I am using JPA with Hibernate as persistence provider,
But #IdClass which i have used is of import javax.persistence.IdClass;
so where is am going wrong,
I have discovered the solution:
The 'id' field in the classes EmployeeEntityTwo and EmployeeId should be same.
// EmployeeId.java;
private Integer id;
should be
// EmployeeId.java;
private Integer employeeId;
I adjusted the getter respectively, and it worked.
From javax.persistence.IdClass JavaDocs:
The names of the fields or properties in the primary key class and the primary key fields or properties of the entity must correspond and their types must be the same.

How to access Object.field in criteria in Hibernate

i have write the criteria for company class.
below are company class, companySearch class and criteria. But criteria list is throw exception. exception is "org.hibernate.QueryException: could not resolve property: san.san of: com.sesami.common.domain.Company". How to access Company.san.san?
Company class
public class Company extends DomainObject implements UserDetails {
private Long id;
private String companyName;
private CompanyType companyType;
private String description;
private String companyURL;
private String billToEmail;
private String hashPassword;
private SAN san;
#OneToOne(cascade = { CascadeType.ALL })
public SAN getSan() {
return san;
}
public void setSan(SAN san) {
this.san = san;
}
...
}
CompanySearch
public class CompanySearch {
private String companyName;
private String email;
private Long san;
private String gstNumber;
......
public Long getSan() {
return san;
}
public void setSan(Long san) {
this.san = san;
}
...
}
Criteria
companyCriteria = this.getSession().createCriteria(
Company.class);
if (companySearch.getSan() != null
&& !"".equals(companySearch.getSan()))
companyCriteria.add(Restrictions.eq("san.san",
companySearch.getSan()));
Integer count = ((Long) companyCriteria.setProjection(
Projections.rowCount()).uniqueResult()).intValue();
companyCriteria.setProjection(null);
companyCriteria.setResultTransformer(Criteria.ROOT_ENTITY);
companyCriteria
.setFirstResult((pager.getPage() - 1) * pager.getPageSize())
.setMaxResults(pager.getPageSize()).list();
List<Company> companies = companyCriteria.list();
PagedResultSet pr = new PagedResultSet();
pr.setPager(pager);
pr.setResultSet(companies);
pr.setRowCount(count);
return pr;
You must create a join to the San entity, using a subcriteria, or an alias:
companyCriteria.createAlias("san", "sanAlias");
companyCriteria.add(Restrictions.eq("sanAlias.san",
companySearch.getSan()));
or
companyCriteria.createCriteria("san").add(Restrictions.eq("san",
companySearch.getSan()));
This is well explained in the Hibernate reference documentation and even in the Criteria javadoc.
Note that this has absolutely nothing to do with Spring, and everything to do with Hibernate. If you searched in the Spring doc for how to do this, no wonder you didn't find anything.

Categories