I am new to hibernate and I am trying to implement one-to-many relationship with cascade loading and updating. However, my code always generates org.apache.jasper.JasperException: org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister exception. Could you take a look on my attempt and suggest what am I doing wrong?
The general idea is that there is a Company class which contains set of customers. When creating new instance of customer, I add him to a company and persist everything.
Entities (only displaying relevant parts, or at least I hope so)
public class Company implements Serializable {
private static final long serialVersionUID = 146243652;
private String id;
private String name;
private String website;
private Set<Customer> customers;
... getters, setters etc
public class Customer implements Serializable {
private static final long serialVersionUID = 864235654;
private String name;
private String surname;
private String adress;
private String id;
private Company company;
customer.hbm.xml:
<hibernate-mapping>
<class name="wa2.entities.Customer" table="CUSTOMER">
<id column="ID" name="id" type="java.lang.String" />
<property column="NAME" name="name" type="java.lang.String" />
<property column="SURNAME" name="surname" type="java.lang.String" />
<property column="ADRESS" name="adress" type="java.lang.String" />
<many-to-one name="COMPANY" class="wa2.entities.Company">
<column name="COMPANY_ID" not-null="true"></column>
</many-to-one>
</class>
</hibernate-mapping>
company.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="wa2.entities.Company" table="COMPANY">
<id column="ID" name="id" type="java.lang.String" />
<property column="NAME" name="name" type="java.lang.String" />
<property column="WEBSITE" name="website" type="java.lang.String" />
<set name="CUSTOMERS" table="CUSTOMER" fetch="select">
<key>
<column name="CUSTOMER_ID" not-null="true"></column>
</key>
<one-to-many class="wa2.entities.Customer"/>
</set>
</class>
</hibernate-mapping>
Saving customer (this worked before the implementation of one-to-many relationship so I doubt that there is anything wrong with this code):
Customer customer = new Customer();
String name = req.getParameter("name");
customer.setName(name);
String surname = req.getParameter("surname");
customer.setSurname(surname);
String adress = req.getParameter("adress");
customer.setAdress(adress);
String companyID = req.getParameter("companyId");
Company company = repository.loadCompany(companyID);
customer.setCompany(company);
String id = UUID.randomUUID().toString();
customer.setId(id);
repository.saveCustomer(customer);
I hope that I did not forget any relevant part. If so, please tell me and I will post the relevant code. Thank you very much for any help!
EDIT:
Thanks for the responses. It seems that I wrote the names wrongly in capitals. I changed that and now I am getting failed to lazily initialize a collection of role: wa2.entities.Company.customers, could not initialize proxy - no Session
since this is an mapping exception, i guess it comes up during session factory creation, and not when saving? also, seeing the whole stacktrace could help. your exception can be caused by a mismatch of property names between mapping file and beans. see Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister.
in your code you have the name attribute values in
<many-to-one name="COMPANY" class="wa2.entities.Company">
<column name="COMPANY_ID" not-null="true"></column>
</many-to-one>
and
<set name="CUSTOMERS" table="CUSTOMER" fetch="select">
<key>
<column name="CUSTOMER_ID" not-null="true"></column>
</key>
<one-to-many class="wa2.entities.Customer"/>
</set>
in uppercase, while the properties in your pojos are named company and customer.
One principle in hibernate, that is often stated, but even more often overseen, is, that you need to keep a clean object tree, or the database will not be kept in sync properly.
I think this causes your problem: You need to keep the tree clean. But you don't: You add the customer to the company on the customer side, but you do not add the customer to the company's customer set.
In your code, you have added a vice-versa link between customer and company. I would add an inverse to the link, for this is what you want.
Second: If you start with hibernate now, really think about the annotation approach. If you inherit legacy apps, stay with the XML, but if you start out fresh ...
Related
I am taking an existing Java application and working on updating it from Hibernate 3 where we used hbm.xml files for Entity Mappings. We are now using Hibernate 5.5.5.Final and the code compiles with ehcache, but now I get an error with the code when starting to run it.
I should start off that one of the Hibernate properties is:
validate
The error message I am getting now is:
org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [my_db_dev.Project_myTemplateInfos]
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:121)
at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89)
at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:200)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:81)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:327)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471)
I would love to completely remove all the hbm.xml files and replace with them with Entity Mapping POJO's with annotations, however, that is not an option right now. The existing application has this different object model that goes throughout, so I don't want to mess with that right now. That will be in the next phase.
According to the error I am missing a table named 'Project_myTemplateInfos' and there is no table with this name. Instead, there is a table named 'Project' and the hbm.xml file for this is as follows.
<hibernate-mapping package="com.myApp.server.model">
<class name="Project" table="project" dynamic-update="true">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" not-null="true"/>
<property name="displayCity" not-null="true"/>
<list name="myTemplateInfos" cascade="all, delete-orphan" lazy="false" >
<key column="projectId" not-null="false" />
<list-index column="listIndex" />
<composite-element class="com.myApp.server.model.MyTemplateInfo" >
<property name="name" not-null="false" />
<property name="frequency" not-null="false" />
</composite-element>
</list>
</class>
</hibernate-mapping>
As you can see 'myTemplateInfos' is a List within the Project table. After the POJO is created, it looks like something like this.
#ModelBean(IProject.class)
#PermissionIdentifier("project")
public class Project extends ModelObject implements Serializable, IProject {
private Long id;
private String displayCity = "";
private List<IMyTemplateInfo> myTemplateInfos = Lists.newArrayList();
// getters and setters
// hashcode and equals
}
Next we do have another table in the database that is called 'myTemplateInfos' and we do have an hbm xml file for that table as follows ... actually we do not have an hbm xml file for this, so maybe that is the issue. I am going to create a hbm xml file for this and see if that solves the problem.
We do have a POJO for this object 'MyTemplateInfo' though.
If I simply remove his List from the hbm mapping and the Project object, the problem goes away of course, but there is another Set in the hbm.xml file which would give me the same problem, but with a new missing table.
The question becomes how to fix this error message. Is the problem within the hbm xml file for 'Project', or is it in the Project POJO, or the fact that an hbm file does not exist for the 'MyTemplateInfo'?
The solution to this was to fix the hbm xml mapping. Since I haven't had to do this in over 15 years, I am very rusty with it. I can't tell you how happy I was back then to switch to Java POJO's for Hibernate Entity classes with Annotations. But now, unfortunately, I am back having to deal with these xml files again.
The table I had 'mycommunitytemplateinfos' I created a new hbm xml file for it as follows, and put this in the hibernate.cfg.xml file before the other hbm xml mapping.
<hibernate-mapping package="com.myapp.server.model">
<class name="MyTemplateInfo" table="mytemplateinfos">
<id name="id" column="projectId">
<generator class="native"/>
</id>
<property name="name" not-null="false" />
<property name="frequency" not-null="false" />
</class>
</hibernate-mapping>
The mapping between this and the actual class is fine as I have tested this out. I put the hbm file in the hibernate.cfg.xml file before the Project.hbm.xml file and modified the Project.hbm.xml with a one-to-many tag as follows:
<list name="myTemplateInfos" cascade="all, delete-orphan" lazy="false" >
<key column="projectId" not-null="false" />
<list-index column="listIndex" />
<one-to-many class="com.myApp.server.model.MyTemplateInfo" />
</list>
And this seemed to work. I had to do something like this a few times until I got the mapping right. In this day of age, there isn't a lot of information about hbm xml files. Hibernate 5 does use these, but I understand that the preferred way is annotated Java POJO's. Unfortunately, I am stuck in a situation where I can't do that yet.
I'm very new to hibernate, and I'm trying to set up a new method in our PersonDAO.
My hibernate mapping file looks like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.foo.bar.domain">
<class name="Person" table="person">
<meta attribute="class-description">A Person</meta>
<id name="id" type="java.lang.Long" column="rid" unsaved-value="null">
<generator class="native" />
</id>
<version name="version" type="integer" column="rversion" unsaved-value="null" />
<property name="UID" type="string" column="UID" length="16" not-null="true" unique="true"/>
<property name="lastName" type="string" column="last_name" not-null="true" />
<property name="firstName" type="string" column="first_name" not-null="true" />
<property name="ownDepartment" type="string" column="own_department"/>
<!-- a person has many responsibilities and a responsibility can can assigned to many person -->
<set name="responsibilities" table="person_responsibility">
<key column="person_id"/>
<many-to-many column="responsibility_id" class="Responsibility"/>
</set>
<set name="additionalDepartments" table="PERSON_TO_ADDL_DEPARTMENT">
<key column="person_id"/>
<element column="ADDITIONAL_DEPARTMENT" type="string"/>
</set>
</class>
and I've written a method like this, in java, to fetch all the managers from a given department:
public List<Person> getManagerByDepartment(final String givenDepartment){
List<Person> l = (List<Person>) this.getHibernateTemplate().executeFind(new HibernateCallback<List<Person>>() {
public List<Person> doInHibernate(Session session) throws HibernateException, SQLException {
String query = "select p from Person p join p.responsibilities responsibilities join p.additionalDepartments additionalDepartments where responsibilities.name = 'manager' and (p.ownDepartment = :givenDepartment or additionalDepartments = :givenDepartment)";
List<Person> result = (List<Person>) session.createQuery(query).setString("givenDepartment", givenDepartment).list();
return result;
}
});
return l;
}
now I do a manual query in SQL, and I can see that for a given department, there are definitely more than one people who have the additional responsibility 'manager'...why does my method only ever return one person, instead of all of them?
I have a strong suspicion that my method, specifically my query, and not the mapping, is the issue, but I can't see what's wrong with it...
I've jumped in at the deep end here, so any help would be very much appreciated.
edit: note, I'm working on hundreds of records, not millions, and this isn't exactly a bottle-neck operation, so I'm not too worried about performance...that said if I'm doing something that's pointlessly wasteful, do point it out
You can print hibernate query by enabling showsql option and check the query getting created and then test it against the database.
Hard to be sure without the sample data, but when you do join in HQL, it is translated to inner join in SQL. So, if you know that there should be more than one result with given responsibility then the problem is probably join p.additionalDepartments.
Try this query with left join for additionalDepartments and see if it works
String query = "select p from Person p join p.responsibilities responsibilities left join p.additionalDepartments additionalDepartments where responsibilities.name = 'manager' and (p.ownDepartment = :givenDepartment or additionalDepartments = :givenDepartment)";
For example, I have the following tables:
USER:
| USERID | USERNAME | USERTYPEID |
USERTYPE:
| USERTYPEID | USERTYPENAME |
So clearly USERTYPEID is a foreign key that a user use to refer to usertype. The JAVA implementation is as such:
I have a class User and a class UserType, where looks like:
public class User {
private int id;
private UserType ut;
....
}
public class UserType {
...
}
In the User.hbm.xml:
<hibernate-mapping>
<class name="com.pretech.User" table="User">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="id" type="int" column="UserID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
So my questions follows:
1) what should I put to map the UserType here and indicate UserType as foreign key?
2) In the case of User includes a list of UserType (may not conceptually true but just want to use as an example), such as:
public class User {
private int id;
private List<UserType> uts;
}
what shoud I do about the hibernate mapping?
EDIT:
Added explanation about foreign key stuff.
There are so many examples available in net for your example, you can also check the hibernate documentation:
For example if you want to have a User entity with a set of UserType's then you can use one-to-many relationship and the mapping file will be:
<class name="User">
<id name="id" column="id">
<generator class="native"/>
</id>
<set name="uts">
<key column="userId"
not-null="true"/>
<one-to-many class="UserType"/>
</set>
</class>
<class name="UserType">
<id name="id" column="id">
<generator class="native"/>
</id>
</class>
And here is another example from the documentation that uses annotations and List instead of Set:
It should be OneToOne or OneToMany depending on UserType is a single reference or multiple (collection list).
Search on Google and you will get numerous tutorials with both xml based and annotations based
I'm new on Hibernate so excuse me for banality but I can't find any answer related to my problem (i tried to search on docs & on SO).
If possible, I would create a left outer join from two tables (t0 and t1) without using HQL; in my case I'd like to use only the Criteria API.
t0 { id, fieldA, fieldB }
t1 { id, fieldC, fieldD }
I don't know which fields will be used for the join, the user can decide it.
I see that the Criteria API has some nice functions such as createAlias or createCriteria but if I use these methods, I can't run a join.
Every table has a class (mapped with a specific hbm.xml) like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
<class name="it.class.T0" table="t0">
<meta attribute="class-description">Table 0</meta>
<!-- Unique id -->
<id name="id" type="long" column="id">
<generator class="native"/>
</id>
<!-- Natural key -->
<natural-id mutable="true">
<property name="fieldA" type="string" column="fieldA" not-null="true" />
<property name="fieldB" type="string" column="fieldB" not-null="true" />
</natural-id>
<!-- Fields -->
<property name="column1" type="long" column="columnd1" not-null="true" />
<property name="column2" type="string" column="column2" not-null="false" />
</class>
</hibernate-mapping>
The entity class is like this:
public class T0 implements Serializable
{
private static final long serialVersionUID = -8123118569358735984L;
public long id;
public String fieldA;
public String fieldB;
public long column1;
public String column2;
public T0()
{
super();
}
}
Is it possible to create a left outer join programmatically (with the Criteria API) without specifying in the hbm.xml (or in a specific HQL) which fields to use?
No. You can't use a hibernate api to create a join based on a mapping hibernate doesn't know anything about. It kind of defeats the purpose of using hibernate in the first place. Why not just write sql then? ;-)
I have the following class:
package lt.vic.valdos.domain.valda;
public class Valda implements java.io.Serializable {
private long id;
private Long valdosKodas;
public long getId() {
return id;
}
public Long getValdosKodas() {
return valdosKodas;
}
}
and the following orm.xml:
<?xml version="1.0"?>
<entity-mappings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.eclipse.org/eclipselink/xsds/persistence/orm http://www.eclipse.org/eclipselink/xsds/eclipselink_orm_2_1.xsd"
version="2.1">
<entity class="lt.vic.valdos.domain.valda.Valda">
<table name="VALDOS" schema="VLD" />
<attributes>
<id name="id" />
<basic name="id">
<column name="vld_id" />
<return-insert return-only="true" />
</basic>
<basic name="valdosKodas">
<column name="valdos_kodas" />
</basic>
</attributes>
</entity>
</entity-mappings>
When I deploy this in glassfish, i get the following error:
Exception [EclipseLink-7215] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Could not load the field named [id] on the class [class lt.vic.valdos.domain.valda.Valda]. Ensure there is a corresponding field with that name defined on the class.
The class is in a jar that is included into a web application as a maven dependency. The orm.xml is in /WEB-INF/classes/META-INF of the web application.
What am I doing wrong?
Figured this one out myself. For some reason EclipseLink requires a setter on a class. Once I add private setters everything seems fine. Why the setters are needed (mapping accessors should default to FIELD) remains a mystery but it is not that important for me. Adding access="FIELD" to all entity attributes also fixes the problem without the setters.
You should be specifying the id as generated using the IDENTITY strategy:
<id name="id">
<column name="vld_id"/>
<generated-value strategy="IDENTITY"/>
</id>
This strategy will automatically read the database provided id back into the new object upon successful commit. The EclipseLink returning statement functionality is only applicable to basic mappings because id is already covered by Identity ID generation.
I think you have to add the column description for the id column to your id element, instead of using an extra basic element. As in <id name="id"> <column name="vld_id" /> ... </id>, without an extra <basic name="id"> ....
From my own experience (some time ago now), it's probably easier to use annotations to define your mappings.