My goal is to create a no PK table in the database (I will never use it for ORM). I only need to create it, that's all.
I know it is possible to create a no PK table with NHibernate from this stack overflow question Why is NHibernate creating a table without primary key? But I need it to work in Java not .Net
Here is what works. It has PK, but I don't need it
<?xml version="1.0" encoding="UTF-8"?>
<hibernate-mapping>
<class entity-name="testtablenopi">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name='xarxint' column='xarxint' type='int' precision='11' scale='0'/>
<property name='xarxbig_decimal' column='xarxbig_decimal' type='big_decimal' precision='17' scale='2'/>
</class>
When I remove <id/> it doesn't work .
I tried replacing class with bag it didn't work.
How can I create a database table without a PI in hibernate?
It's not possible. Hibernate and all ORMs I know require a PK to be defined for an object.
You'll need to use a plain old JDBC insert statement.
Related
I added a new CLOB Column in a table and modified my hbm.xml - file to use this new column (the old column was varchar2 and therefore too small):
(I used random-names for demonstration purposes)
<?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="test.com.TestDO" table="TESTTABLE">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="sequence">
<param name="sequence">SEQ_SEQUENCENAME</param>
</generator>
</id>
</property>
<property name="columnName1" type="java.util.Date">
<column name="COLUMN_NAME1"/>
</property>
<property name="columnName2" type="java.lang.String">
<column name="COLUMN_NAME2"/>
</property>
<property name="columnName3" type="java.lang.String">
<column name="COLUMN_CLOB"/>
</property>
</class>
</hibernate-mapping>
As you can see, my clob-column (COLUMN_CLOB) is defined as last, because first I got this exception:
ORA-24816: Expanded non LONG bind data supplied after actual LONG or LOB column
I searched for this error and the solution was to place the clob binding at the last in the insert-statement, so I defined the clob-column in the hbm.xml file to be last, as you can see above.
Now I am not getting the ORA-24816 exception, but I am getting this exception:
ORA-01461: can bind a LONG value only for insert into a LONG column
The generated Insert-Statement looks like this:
insert into TESTTABLE(COLUMN_NAME1, COLUMN_NAME2, COLUMN_CLOB, ID) values (?, ?, ?, ?)];
As you may have noticed, the COLUMN_CLOB - column does not appear last in the generated Insert-Statement. I don't know if the order in the hbm-xml -file affects the order of the columns in the generated insert-statement?
But anyway, I am not getting the ORA-24816 anymore just ORA-01461. I don't know how to solve this issue.
I am using Hibernate 3 and Oracle Version: Enterprise Edition Release 11.2.0.4.0 - 64bit Production
EDIT:
I have the exact same issue as described here:
http://newtechnobuzzz.blogspot.ch/2014/07/ora-24816-expanded-non-long-bind-data.html#.Wcy-QdFpHRY
I have tried the following solutions:
Chaning the order of getter and setter methods does not work
Changing the order of the declared property in the hbm.xml -file does
not work
The problem described on the link and on other sites, state that this excpetion
occurs if you try to insert data in both the varchar2 and the clob columns (2 strings with a length of 4000). However I am only inserting data into the clob-column which is bigger then 4000. I am not using the old varchar2 - column anymore.
Now, I am trying to solve this by using this solution:
It can solved by writing two update queries. Firstly, save/update the entity by an Update query and then write another query to update LOB columns in that entity.
However, I dont quite understand, how I should modify my code. My method looks like this:
#Override
#Transactional(readOnly=false, propagation=Propagation.MANDATORY)
public TestDO saveTest(TestDO test) {
getHibernateTemplate().saveOrUpdate(test);
return test;
}
From what I can see, the property type has not been declared correctly for the CLOB column, try that:
<property name="columnName3" length="100000" type="StringClob">
<column name="COLUMN_CLOB"/>
</property>
TL;DR How can I enforce the Hibernate schema creation to create a foreign key constraint in a table-per-concrete-class setup from AbstractProperty.ownerId to Owner.ownerId for the structure displayed below, without adding a Owner property to the AbstractProperty?
I'm working on a project where I have the following class structure:
The Owner has a one-to-one mapping to an AbstractProperty, which is extended by the ConcreteProperty class (and others like AnotherProperty, but that's not really relevant for the rest of this question).
The AbstractProperty really has only one property, the abstractPropertyId. Therefor, we want to use the table-per-concrete-class structure, ending up with the tables Owner, ConcreteProperty, and tables for the other AbstractProperty extending classes (AnotherProperty).
To this end, I created the following mapping for Owner:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example">
<class name="Owner">
<id name="ownerId">
<generator class="identity"/>
</id>
<property name="ownerProperty"/>
<one-to-one name="abstractProperty"/>
</class>
</hibernate-mapping>
And for the AbstractProperty:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example">
<class name="AbstractProperty" abstract="true">
<id name="ownerId">
<generator class="foreign">
<param name="property">ownerId</param>
</generator>
</id>
<union-subclass name="ConcreteProperty">
<property name="concreteProperty"/>
</union-subclass>
<union-subclass name="AnotherProperty">
<property name="anotherProperty"/>
</union-subclass>
</class>
</hibernate-mapping>
This works.
However, and here is my question, using this mapping and having Hibernate create the schema for me (<property name="hbm2ddl.auto">create</property>), it does not create a foreign key constraint from the ConcreteProperty.ownerId database field to the Owner.ownerId field. It does when I create the inverse constrained one-to-one field from the AbstractProperty to Owner using this mapping for AbstractProperty (where the owner field is of type Owner in the AbstractProperty java class):
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example">
<class name="AbstractProperty" abstract="true">
<id name="ownerId">
<generator class="foreign">
<param name="property">ownerId</param>
</generator>
</id>
<one-to-one name="owner" constrained="true"/>
<union-subclass name="ConcreteProperty">
<property name="concreteProperty"/>
</union-subclass>
<union-subclass name="AnotherProperty">
<property name="anotherProperty"/>
</union-subclass>
</class>
</hibernate-mapping>
How can I enforce the creation of a foreign key from AbstractProperty.ownerId to Owner.ownerId without this Owner field in my AbstractProperty?
Simple answer: Never let Hibernate create the schema for real applications.
Hibernate is an object relational mapper and one should treat it as this.
That Hibernate additionally creates schemas is at most kind for the first time. But in environments after the first release you don't want to have Hibernate control over the schema. After all you have to handle SQL to have migrations scripts (manually or tool support). After the first release you will have data in the database. To ensure data migration on the production system with less problems you should consider schema and data migration the same way in the production environment as you do in your development environment.
Exceptions of that can be any application that has rarely changing data that may be reconstructable fast on data loss.
We use standard JPA (no hibernate-specific hacks) and had the same problem, and we did not find a nice solution.
Assumptions:
AbstractProperty is a class in a package that is reusable/shared in different applications, and that you don't want a reference to an application specific Owner Class.
ConcreteProperty & AnotherProperty is application specific.
In this case, the solution would be to put references in ConcreteProperty to the Owner (with foreign keys), eventually both with AnotherProperty extending the same ApplicationProperty, and making the abstractPropertyId private, so that on setting the Owner, it is set automatically.
Wouldn't it work automatically if you define the Owner attribute on Abstract Property as "transient"?
Variables may be marked transient to indicate that they are not part of the persistent state of an object.
And if you implement your own manual serialization you could check for the Modifier on a field and just ignore it --> cyclic serialization problem avoided.
The only other way I see is to push the Owner attribute to each of the concrete Property classes and change your mapping to
<class name="AbstractProperty" abstract="true">
<id name="ownerId">
<generator class="foreign">
<param name="property">ownerId</param>
</generator>
</id>
<union-subclass name="ConcreteProperty">
<property name="concreteProperty"/>
<one-to-one name="owner" constrained="true"/>
</union-subclass>
<union-subclass name="AnotherProperty">
<property name="anotherProperty"/>
<one-to-one name="owner" constrained="true"/>
</union-subclass>
</class>
which creates the following sql:
create table AnotherProperty (
ownerId integer not null,
anotherProperty varchar(255),
primary key (ownerId)
)
create table ConcreteProperty (
ownerId integer not null,
concreteProperty varchar(255),
primary key (ownerId)
)
create table Owner (
ownerId integer generated by default as identity,
ownerProperty varchar(255),
primary key (ownerId)
)
alter table AnotherProperty
add constraint FK_ceq89n6x2i1ax18bb4gqpq4m5
foreign key (ownerId)
references Owner
alter table ConcreteProperty
add constraint FK_i41buhvtxxtpsim2cc0ur1gxr
foreign key (ownerId)
references Owner
First : Hibernate/JPA is capable of handling a lot of scenarios - if there really was a lot of folks trying the same approach as you, I would think that it would have been addressed by now - it's no spring chicken. ** that's a clue ;-) **
Second : Having a table called 'Owner' with a 'ownerProperty' is another clue. The names infer a built in relationship.
Third : Simply by stating that you don't want an owner property within the AbstractProperty table, this sets the stage for a logical fallacy commonly referred to as a catch-22 (http://en.wikipedia.org/wiki/False_dilemma).
My point -> This seems to be more of a modeling/design issue than a technical/framework issue.
My suggestion would be to take a step back from the problem, and re-evaluate it. For example, if you were just writing straight queries with spring-jdbc, how would you expect to interact with the data for SELECT, UPDATE, and DELETE operations? ... if you work through those, your solution/needs would likely present themselves more clearly. To be even more pointed, what would you expect the behavior to be on a cascading-delete? If I issue a DELETE statement on a single Owner record, would you like for the database to automatically delete the records from the child tables? recursively? Once you have that isolated, you can figure out how to tell Hibernate what to do - don't allow team members to put the cart before the horse by prematurely restricting the solution.
For example, (case 1 ) if you're really dealing with a 'property' of an owner, it would be reasonably foreseeable that you would need to store multiple properties about an owner (aka: OneToMany).
Alternatively, (case 2 ) if you're dealing with a 'type' of owner ( as in a discriminator field ), then your 'AbstractProperty' table should be extending Owner ... in which case you may be able to reduce your solution down to 3 tables ( Owner with a discriminator, Concrete1 w/ ownerId, Concrete2 w/ ownerId ).
Proposed Solution 1 : In either of those cases, it would still make sense for the 'AbstractProperty' table to have a reference to it's parent/Owner. And if it did, I think Cascading DELETES may work the way that you would prefer.
Proposed Solution 2 : If however, in a cascading delete scenario of an Owner record, you would prefer that the row(s) in AbstractProperty stick around as reference-data, then it could be argued that you should put an additional table between Owner and AbstractProperty to protect your reference data ... as a mapping table that has a unique composite key.
Focus on the business needs and user-stories, and that should hopefully guide your choice of available solutions.
I am using Hibernate 3.2.5. I am getting the above exception while using many-to-one mapping. The training table is having a many to one relation with Department table, i.e. One Depatement is capable of taking more than one training.
The exception is asking me to add insert="false" update="false" in my hbm file. If I add this bit in hbm file, then the code works fine.
Here is the hbm file:
<?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="com.infy.model.Training" table="training">
<id name="Id" type="integer" column="ID">
<generator class="assigned"></generator>
</id>
<property name="trainerName">
<column name="TRAINER_NAME"></column>
</property>
<property name="deptId">
<column name="DEPT_ID"></column>
</property>
<property name="trainingSubject">
<column name="TRAINING_SUBJECT"></column>
</property>
<many-to-one name="departmentDetails" column="DEPT_ID"></many-to-one>
</class>
</hibernate-mapping>
If I change this line to:
<many-to-one name="departmentDetails" column="DEPT_ID" insert="false" update="false"></many-to-one>
Then the code works. I want to know what is the exact reason for adding this.
Regards,
You have mapped the DEPT_ID column twice, here:
<property name="deptId">
<column name="DEPT_ID"></column>
</property>
And here:
<many-to-one name="departmentDetails" column="DEPT_ID"></many-to-one>
When executing a select statement, Hibernate will be fine populating two properties of your object from the same column, however when doing an insert or an update it cannot decide which property to persist in the database.
Why do you need two properties mapped to the same column in the first place? If you need access to the deptId, you can probably remove the deptId property and instead do
training.getDepartmentDetails().getId()
The error message for this scenario is quite clear (you haven't put it here, but I've seen it a few times). The problem is that you've mapped the column DEPT_ID to two different fields in your class.
First, you've mapped it to the property deptId and then to departmentDetails. As you found out, hibernate allows to do this only if one of the mappings is configured to be insert="false" update="false".
The reason is quite simple. If you would change deptId to another id, hibernate would need to change the class that is mapped in departmentDetails, which is quite complicated.
if you need to get the deptId, you can add a getDeptId method on Training that returns departmentDetails.getId(). And don't provide a setDeptId.
If you are using the same column name twice in your mapping file. might be you get mapping Exception
Initial SessionFactory creation failed.org.hibernate.MappingException:
Also if u mark insert=flase and update=false .
if u try to update or insert in records in table or another legacy system try to update these column value. it wouldn't update or insert that filed.
Please check the below link .it will help to find your solutions.
http://www.techienjoy.com/hibernate-insert-update-control.php
Thanks
Sandeep G.
I am trying to implement persistence of some Java objects via Hibernate mapping to a MySQL table. When I commit I get a message saying 'Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1'.
My hypothesis is that the problem is caused from having a long-field in my Java POJO that I want to use as my primary key in the MySQL table. Since I was not able to use datatype LONG as my primary key in MySQL table (ERROR 1170: BLOB/TEXT column 'id' used in key specification without a key length) I concluded from some googling and this post that BIGINT would be the suitable mapping for long. However it is not updating.
My test POJO Personis very simple. It has 3 fields: id (long), firstname (String), lastname (String) with setters and getters, etc.
I do the hibernate mapping in xml (person.hbm.xml) that essentially looks like (minus headings):
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="native"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
My actual java code snippet that is supposed to save or update the record is simple:
Transaction tr = session.beginTransaction();
Person person = new Person(1,"John","Doe");
session.saveOrUpdate(person);
tr.commit();
And here's that thing, this all works just fine if I change the type of id to an int (Integer) in the Person object and in the MySQL table. However, I do not have that option for the actual objects that I want to persist so the question is; what am I doing wrong or what should I do to get it to work? Thanks.
ADDING Stacktrace:
Hibernate: update hibernatetest set firstname=?, lastname=? where id=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:57)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3006)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2908)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3237)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:187)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1082)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:317)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at com.hibernate.test.TestMain.main(TestMain.java:38)
nested transactions not supported
UPDATE:
OK, I have finally worked it out. I changed the hibernate generator class from 'native' to 'assigned' and now it works as expected. So now the hibernate mapping looks like:
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="assigned"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
Must admit I did not know the meaning of that parameter (copied from somewhere) and had no idea it could cause this much headache. Found this explanation which was quite useful.
Apparently I do not have enough credentials to answer my own questions so I guess that it will remain open or if someone provides an empty answer, I will accept it. Thanks.
When you use the saveOrUpdate() method hibernate fires the insert query if the id of the object is null and update if it is any other value. I can see the code,
Person person = new Person(1,"John","Doe"); setting the id to 1 and calling the saveOrUpdate() method. I am assuming there are no entries for the id 1 and hence the error is thrown.
To make it work, you need to make the below changes.
Change the Type of id in person to Long from long(The wrapper class so that it can support null).
Write the constructor new Person("John","Doe"); and save that object.
It is not a good Idea to keep the <generator class="assigned"/> for the transactional data. Instead you should be sticking to the native as you were trying first.
I feel this is a cleaner way to solve your initial problem, even though you have found an alternate solution.
I am having some trouble with returning a non-empty Set into an object using Hibernate and a custom CompositeUserType key.
I have a set of tables and views (simplified here):
create table lang (lang_id,lang_cd);
create table article (art_id,...);
create table article_lang (art_id, lang_id,title,...);
create view article_lang_vw (select * from article join article_lang on art_id);
create table authors(user_id,...);
create table article_authors(art_id,lang_id,user_id);
And database functions:
create or replace procedure addarticle(title,art_id,lang_id) ...
create or replace procedure updatearticle(title,art_id,lang_id)..
create or replace procedure delarticle(art_id,lang_id)..
create or replace procedure addarticleauthor(user_id,art_id,lang_id)...
create or replace procedure delarticleauthor(user_id,art_id,lang_id)...
So to accomplish this mapping using those functions I had to implement CompositeUserType so now I have Java classes like this:
class ProcedureGenerator implements PostInsertIdentityGenerator ...
class Language { int lang_id }
class ArticleLangPKType implements CompositeUserType { //implemented methods }
class ArticleLangPK { int art_id; Language l; }
class Article { ArticleLangPK id; String title; Set<Author> authors; }
class Author { int user_id; String name; }
I want to have a List or Set of Authors. But cannot figure out how to map this part in the *.hbm.xml files. It currently looks something like this:
<class name="Author" mutable="false">
<id name="user_id"/>
<property name="name"/>
</class>
<class name="Article">
<id name="id" type="ArticleLangPKType">
<column name="art_id"/>
<column name="lang_id"/>
<generator class="ProcedureGenerator"/>
</id>
<property name="title"/>
<set name="authors" table="article_authors">
<key> <!-- <key type="ArticleLangPKType"> -->
<column name="art_id"/>
<column name="lang_id"/>
</key>
<many-to-many class="Author" table="article_authors" unique="true"/>
<!-- addauthor, delauthor sql here some how -->
</set>
<sql-insert callable="true">{call addarticle(?,?,?)}</sql-insert>
<sql-update callable="true">{call updatearticle(?,?,?)}</sql-update>
<sql-delete callable="true">{call adddelete(?,?)}</sql-delete>
</class>
But when I run this session.load(Article.class, pk) on an article I know has authors I get a Set size of zero. Otherwise I have no problems inserting, updating, deleting using Hibernate, but now I am stumped. This seems to me to indicate a problem with my ArticleLangPKType.
Any ideas what to do to complete this? Why is my Set always size 0? How would I save the author using the Article's Set and the SQL procedures as provided? Is Hibernate right for me? Do I need a break to see this clearly?
Thanks in advance!
Nevermind I did need a long break. My ArticleLangPK did not override hashCode and Equals correctly. Now just to figure out how to call those other two stored procedures correctly.