I am new to Hibernate and was writing some test program.
I am wondering if its a must to have a table , one column of which will be updated using some kind of sequence.
For ex. I created a table
create table course(course_name varchar2(20));
and when I am defining Course.hbm.xml in the following way
<class name="Course" table="COURSE" >
<property name="course">
<column name="course"/>
</property>
</class>
I am getting an error in the XML file saying a declaration of "id" or something similar is expected. I can give the whole error message if required.
You need an ID column so hibernate can identify that row in the table. I'm not fluent in that oldschool hibernate xml mapping but it should look roughly like that:
create table course(id integer primary key, course_name varchar2(20));
<class name="Course" table="COURSE" >
<id name="id">
<!-- uses sequence, auto increment or whatever your DBMS uses for id generation -->
<generator class="native"/>
</id>
<property name="course">
<column name="course"/>
</property>
</class>
As a side note: mapping your entities with annotations is a bit more common nowadays. Makes it easier, especially for starters.
Related
Maybe someone faced the same situation? In my project I have two services. I create database tables using liquebase. When creating a table of permissions, I immediately enter 4 values there using inserts. and everything is buzzing. So, now in the second service (admin panel) I created an apish that allows you to create permissions. When I try to make a record in the table of permissions, in which there are already 4 records, I get an error ERROR: duplicate key value violates unique constraint "permissions_pkey". The error occurs if I make a request 5 times, the record is added for the 5th time. Thanks in advance)
<insert tableName="permissions">
<column name="id" value="1"/>
<column name="name" value="USERS_READ"/>
</insert>
<insert tableName="permissions">
<column name="id" value="2"/>
<column name="name" value="USERS_GET"/>
</insert>
<insert tableName="permissions">
<column name="id" value="3"/>
<column name="name" value="USERS_CREATE"/>
</insert>
<insert tableName="permissions">
<column name="id" value="4"/>
<column name="name" value="USERS_DELETE"/>
</insert>
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
If it's an identity column, you don't have to provide values for the id column in the liquibase script. Let the DB calculate them for you. That's the whole point of using an identity column, after all.
If you need to reference the ids later in the script somehow, look up the way of obtaining the last inserted id from the RDBMS you're using, quite often there will be a method like LAST_INSERTED_ID(). Or, look up the inserted rows using the name column.
You are using generation type IDENTITY, mapping to data type serial in postgres. When you want to insert permissions created from your admin panel, postgres uses a sequence, starting at 1 from your description and the id 1 through 4 are already used by your manual inserts. Leave the ids out of your manual inserts, or explicitly set ids on your entitities created from your admin panel by eg. querying the current maximum id used in the table. Consider changing the generation type.
As the subject says I want to map a HashMap with a List as a value in Hibernate.
Since it looks like it is not possible to have a collection as element in a map in Hibernate, I have created a class that contains a list of Ids.
My HashMap looks like this:
Map<Status, IdList> statusIdsMap = new HashMap<Status, IdList>();
Status is either insert/update/remove. IdList contains a List of Ids.
Hibernate mapping looks like this:
<id name="id" type="IdType" />
...
<map name="statusIdMap">
<key column="id" />
<map-key type="Status" column="StatusCode" />
<element type="IdList" />
</map>
...
<class name="IdList" select-before-update="true" table="IdList">
<id name="id" column="id" unsaved-value="null">
<generator class="IdGenerator"/>
</id>
<set name="Ids" table="IdsForStatus">
<key column="id"/>
<element column="updatedId" type="IdType"/>
</set>
</class>
If I try to store the class with statusIdMap, Hibernate generates an Insert SQL that tries to put the IdList class in a column, instead of the Id to the IdList class.
The Hibernate error message says "expected number, got binary".
It's related question to this
Hibernate(JPA) mapping a HashMap
where the answer suggests to put list in the Key class, but it's not something I wish to do.
Any other suggestions?
EDIT:
In short the test does following:
Fills object with dummy values, including map.
Initializing hibernate and starts a transaction
Inserts object
Commits transaction and closes hibernate session
The error message comes from the database when trying to commit, since the generated insert statement is wrong as mentioned above.
There are 3 pojo namely EmployeeMaster (the parent class), Hr and Personal (the child class of EmployeeMaster).
Then I have 3 separate tables for each pojo, constructed as :
create table emp_master(emp_code integer,emp_name text,emp_desig text,
emp_dept text,primary key(emp_code));
create table hr(emp_code integer,salary integer,hra integer,da integer,
taxes integer,grade text,foreign key(emp_code) references emp_master(emp_code));
create table personal(emp_code integer,address text,married bool,
foreign key(emp_code) references emp_master(emp_code));
emp_code is the primary key for emp_master and it is the foreign key for both hr and personal table.
I constructed 3 separate jsp forms to take in the data.
Following is the hibernate mapping file :
<hibernate-mapping>
<class name="pojo.EmployeeMaster" table="emp_master">
<id name="emp_code">
<generator class="assigned" />
</id>
<property name="emp_dept" />
<property name="emp_desig" />
<property name="emp_name" />
<joined-subclass name="pojo.Hr" table="hr">
<key column="emp_code" />
<property name="da" />
<property name="grade" />
<property name="hra" />
<property name="salary" />
<property name="taxes" />
</joined-subclass>
<joined-subclass name="pojo.Personal" table="personal">
<key column="emp_code" />
<property name="address" />
<property name="married" />
</joined-subclass>
</class>
</hibernate-mapping>
Now the problem is,I do not want any null row and want to submit data for the same emp_code into other tables namely hr and personal.
But as I try to submit data into the hr table for emp_code 101, that already exists in the master table I get an error saying Duplicate entry '101' for key 'PRIMARY'.
If I change the generator class to increment in the mapping xml, I get a row with null values,for the data inserted from the child class.What I want is an entry into the three tables with the same employee code. How do I do this ?
Your hibernate mapping suggests that you EmployeeMaster, HR and personal forms a hierarchy, i.e. EmployeeMaster is parent class, while HR and Personal are child classes.
There is something wrong in what you want to achieve and what your mapping suggesting. In above mapping, an ID can be mapped to either HR or to Personal and not both.
You should consider changing the mapping from INHERITANCE to ASSOCIATION.
As per your mapping xml, your java classes should be defined as follows:
public class EmployeeMaster {}
public class HR extends EmployeeMaster{}
public class Personal extends EmployeeMaster{}
Now, you have to device how you can fit your requirements into above model. If that is not the case, then you have no choice but to change the model (xml mapping as well)
I have the following domain objects
Loan {
int id;
Date attribute1;
}
LoanExtension {
Date attribute2;
}
I would like to keep my objects like this because sometimes I would like to change only the attributes in LoanExtension in my database (i.e. attribute1 in the loan object will be null in the object and i don't want this to get set in the database).
How is this possible using a hibernate mapping with xml? I have done the following
<class name="org.domain.borrowerReview.Loan" table="loan_profiles" >
<cache usage="read-only"/>
<id name="loanId" column="id">
<generator class="native"/>
</id>
<version name="attribute1" column="date_1/>
<subclass name="org.domain.borrowerReview.LoanExtension" extends="org.rangde.domain.borrowerReview.LoanProfilesUpdate">
<property name="attribute2" column="date_2" />
</subclass>
</class>
I'm getting this exception :
Discriminator is needed when 'single-table-per-hierarchy' is used and a class has subclasses
Short answer
You need to add a discriminator column and change the snippet to something like this.
<class name="org.domain.borrowerReview.Loan" table="loan_profiles" >
<cache usage="read-only"/>
<id name="loanId" column="id">
<generator class="native"/>
</id>
<version name="attribute1" column="date_1/>
<discriminator column="loan_profiles_type" type="string"/>
<subclass name="org.domain.borrowerReview.LoanExtension" extends="org.rangde.domain.borrowerReview.LoanProfilesUpdate">
<property name="attribute2" column="date_2" />
</subclass>
</class>
Long answer
First off, do remember that the inheritance in JPA is not an absolute parallel to regular inheritance.
One must evaluate how the design of classes will have an impact on the underlying schema.
You have not mentioned how you would like to have your table structure as.
Hibernate provides three mechanisms for inheritance.
table per class hierarchy
table per subclass
table per concrete class
See detaiils here
Your xml snippet suggests that you are using table per class hierarchy.
Now, if one has subclasses, there would be little use if the is just one subclass (though it is allowed). And under "table per class hierarchy" all the sub classes are placed in the same table.
To distinguish between which subclass a particular record belongs to, hibernate relies on discriminator column.
You need to define the same.
Refer the following documentation documentation.
I have a user object that has a one-to-many relationship with String types. I believe they are simple mappings. The types table hold the associated user_id and variable type names, with a primary key 'id' that is basically a counter.
<class name="Users" table="users">
<id column="id" name="id" />
...
<set name="types" table="types" cascade="save-update">
<key column="id" />
<one-to-many class="Types" />
</set>
</class>
<class name="Types" table="types">
<id column="id" name="id" />
<property column="user_id" name="user_id" type="integer" />
<property column="type" name="type" type="string" />
</class>
This is the java I used for adding to the database:
User u = new User();
u.setId(user_id);
...
Collection<Types> t = new HashSet<Types>();
t.add(new Type(auto_incremented_id, user_id, type_name));
u.setTypes(t);
getHibernateTemplate().saveOrUpdate(u);
When I run it, it gives this error:
61468 [http-8080-3] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
61468 [http-8080-3] ERROR org.hibernate.util.JDBCExceptionReporter - Duplicate entry '6' for key 'PRIMARY'
61468 [http-8080-3] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
When I check the sql, it shows:
Hibernate: insert into users (name, id) values (?, ?)
Hibernate: insert into types (user_id, type, id) values (?, ?, ?)
Hibernate: update types set id=? where id=?
Why does Hibernate try to update the types' id?
The error says: Duplicate entry '6' for key 'PRIMARY', but there really isn't? I made sure the ids are incremented each time. And the users and types are added into the database correctly.
I logged the information going in, and the types added has an id of 7 and a user id of 6. Could it be that Hibernate takes the user_id of 6 and tried to update types and set id=6 where id=7? Therefore the duplicate primary key error?
But why would it do something so strange? Is there a way to stop it from updating?
Should I set the id manually? If not, then how should I add the types? It gives other errors when I add a type object that only has a type string in it and no ids.
Thanks guys. Been mulling over it for days...
Your biggest problem is incorrect column in the <key> mapping - it should be "user_id", not "id". That said, your whole mapping seems a bit strange to me.
First of all, if you want IDs auto generated you should really let Hibernate take care of that by specifying appropriate generator:
<id column="id" name="id">
<generator class="native"/>
</id>
Read Hibernate Documentation on generators for various available options.
Secondly, if all you need is a set of string types, consider re-mapping them into a collection of elements rather than one-to-many relationship:
<set name="types" table="types">
<key column="user_id"/>
<element column="type" type="string"/>
</set>
That way you won't need explicit "Types" class or mapping for it. Even if you do want to have additional attributes on "Types", you can still map it as component rather than entity.
Finally, if "Types" must be an entity due to some requirement you have not described, the relationship between "Users" and "Types" is bi-directional and needs to be mapped as such:
<set name="types" table="types" inverse="true">
<key column="user_id"/>
<one-to-many class="Types"/>
</set>
...
in Types mapping:
<many-to-one name="user" column="user_id" not-null="true"/>
In the latter case "Types" would have to have a "user" property of type "Users".
Here is a detailed example.
The solution that worked for me was to separately save the two parts (without adding type to user):
getHibernateTemplate().save(user);
getHibernateTemplate().save(new Type(user.id, type_name));
And with the < generator class="native" /> on only the types id.
Bad practice?
Had mapped it as a collection of elements, but it was somewhat wrongly adding the user_id to the types id column which causes duplicate error sometimes; since types id is the only primary key column. Weird! I sort of remember there was some other column error, but forgot about it, because I immediately reverted back to one-to-many. Couldn't grasp the strange workings of Hibernate...
I will try the bi-directional solution sometime... Thanks a lot for all the help :)