I'm working with JPA annotations, and I'd like to use an annotation that behaves the same as the Hibernate #Where.
I want to map an entity from a table, but I have to do add a where clause in every query that uses that table.
This is my case: my table has a column deleted, and when this column is 1, this row should not be appear.
If I'd use Hibernate hbm, this is resolved in this way:
<hibernate-mapping>
<class
name="MyClass"
table="MyTable"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
</hibernate-mapping>
But I don't know how to do this only with JPA.
The only idea that comes to me is to extend the JpaRepository of this entity, but my problem is that there is another entity that uses this one, so extending the JpaRepository of the first entity is useless.
Using the Where clause in the column should work, but I'm trying to find a way to do this without Hibernate.
Anyone have any idea?
No, there isn't anything like that in JPA.
But there's really nothing wrong in having a few provider specific annotations here and there that do the job.
Related
I am working migrating project to a pure JPA annotation based mapping from an XML mapping and I am running into an issue when trying to delete (remove) and entity and its children. It works in with XML mapping and does not work with the annotation mapping.
The XML mapping looks like this:
<set name="evaluations" order-by="evalDate desc" table="Evaluation" lazy="true" inverse="true" cascade="delete">
<key column="requestId" />
<one-to-many class="org.stuff.model.Evaluation" />
</set>
The annotation mapping, as far as I can tell is this:
#OneToMany(orphanRemoval=true)
#JoinColumn(name = "requestId")
#OrderBy("evalDate DESC")
private Set<Evaluation> evaluations = new TreeSet<>();
This is a uni-directional relationship.
The JPA code to delete the entity is:
ServiceRequest sr = em.getReference(ServiceRequest.class, id);
em.remove(sr);
Where the above Evaluation is a child object of ServiceRequest. Hibernate 4.3.7 is the JPA Impl I am using, running on WildFly 8.2.
With Hibernate set to barf out its SQL, executing the remove with the annotation mapping in place Hibernate produces a query to look up the Entity reference and then when the remove is called it produce an update trying to update the child record in Evaluation FK back to ServiceRequest to be null:
Hibernate: update Evaluation set requestId=null where requestId=?
And that blows up because there is a not null constraint on requestId.
If I do the same operation using the XML mapping (see above snippet) it works just fine. All child entities are deleted along with the parent. and Hibernate only produces selects and deletes, if never tries to update anything.
This feels like I have the annotation mapping wrong, but I cannot figure where I went wrong. Please help.
You xml config actually says the relationship between your ServiceRequest and Set are bi-directional because inverse = "true".
But your JPA annotation is uni-directional. so this should work (edited after OP's comment)
#OneToMany(orphanRemoval=true,mappedBy="requestId")
#OrderBy("evalDate DESC")
private Set<Evaluation> evaluations = new TreeSet<>();
Here mappedBy="requestId"tells Hibernate that this is owner side of the relationship. So it will issue statement to remove Evaluation.
I think you need to specify the cascade annotation. Beware of this issue though.
Thanks #troy for some direction. The addition of the cascade alone didn't work, but adding insertable=flase, updateable=false did. So the annotation mapping now looks like this:
#OneToMany(cascade=CascadeType.REMOVE)
#JoinColumn(name = "requestId", insertable=false, updatable=false)
#OrderBy("evalDate DESC")
private Set<Evaluation> evaluations = new TreeSet<>();
I am not sure exactly why this works, so if someone can explain it I would be very grateful.
I got here rather indirectly. First I added a nullable-false to this mapping and when I deployed it Hibernate complained about it and told me I needed to add insert=false update=false to requestId on the Evaluation entity. That sort of worked. I could delete like I wanted to, but I couldn't save or insert an evaluation. I kind of expected that to happen. So I just tired this solution and it worked.
#Entity
class MyEntity {
}
hibernate.hbm2ddl.auto=update
Is it possible that hibernate will generate a CREATE unlogged TABLE statement instead of CREATE TABLE when the table for MyEntity does not exist?
I think you can do this by subclassing the relevant Postgres dialect and overriding the getCreateTableString() method as below.
public class CustomPostgresSQLDialect extends PostgresSQLxxDialect{
#Override
public String getCreateTableString() {
return "create unlogged table";
}
}
And obviously set this as your dialect:
<property name="hibernate.dialect" value="org.foo.CustomPostgresSQLDialect "/>
I think there's no param in #Entity or #Table to pass that kind of options to the DBMS. Found this feature request in hibernate's jira though, it covers your use case, maybe you want to vote for it.
Anyway you can try and add an auxiliary database object in the hibernate configuration to run ALTER TABLE MyEntity SET UNLOGGED, it's the easiest way i can think of doing that DDL modification.
You should use update rather than create
this is helpful:
Hibernate hbm2ddl.auto default value
hi I have an entity with this annotation
#Entity
#Table(name = "REPORT_WORK")
But for some reason hibernate keep saying Missing Table: REPORT_REPORT_WORK
I know that is the problem because of when I change the name to "REPORT_WORKX"
It will say Missing Table: REPORT_REPORT_WORKX
Has any encountered this issue before?
Update: when I change the name to JJJJ
It will say Missing Table: REPORT_JJJJ
so for some reason there it is auto appending REPORT_
Configuration:
hibernate.hbm2ddl.auto=validate
I suspect that the problem is your Hibernate configurations. Specifically, if you don't have an appropriate setting for hibernate.hbm2ddl.auto, Hibernate won't automatically update the database schema when you change your model.
(And if you don't want the updates to happen automatically, then you need to figure out what schema changes are needed, code them as SQL DDL, and run them manually.)
Can you post your persistence.xml (or equivalent)?
It sounds like you are implementing org.hibernate.cfg.NamingStrategy, get rid of this configuration.
Some additional info:
JPA (Hibernate) and custom table prefixes
I would like to evaluate JPA on an existing project. The database model and the java classes exists and are currently mapped via self generated code. The database model and the java classes do not fit ideally together - but the custom mapping works well. Nevertheless the usage of JPA in general seems worth a try.
As you see I am new to JPA and have to do the work with xml configuration. Currently I am working on a one-to-many unidirectional relationship using a join table (please do not discuss this szenario here).
A (one - relationship owner) <-> AB (JoinTable) <-> B (many)
The tables look like this
A
--
ID
BREF
...
B
--
ID
...
AB
--
A_BREF (foreign key to a reference column in A which is NOT the id)
B_ID
I would like to define a unidirectional one-to-many relationship for class A.
class A {
private List<B> bs;
}
and did it like this:
<one-to-many name="bs">
<join-table name="ab">
<join-column name="a_bref">
<referenced-column-name name="bref" />
</join-column>
<inverse-join-column name="b_id">
<referenced-column-name name="id" />
</inverse-join-column>
</join-table>
</one-to-many>
Althoug this does not force an error it is not working. The problem is that the join table does not work on the ID column of A. The query to select the "B" entities works with the A.ID column value instead of the A.BREF column value to select the entities.
(How) can I make this mapping work (I use eclipselink 2.2.0)?
Thanks for any suggestion!
EDIT:
After looking at a link provided in #SJuan76 answer I slightly modified my mapping to
<one-to-many name="bs">
<join-table name="ab">
<join-column name="a_bref" referenced-column-name="bref" />
<inverse-join-column name="b_id" referenced-column-name="id" />
</join-table>
</one-to-many>
This now causes the following errors (tested with eclipselink 2.1.0 and 2.2.0)
eclipselink 2.1.0
Exception Description: The parameter
name [bref] in the query's selection
criteria does not match any parameter
name defined in the query.
eclipselink 2.2.0
Exception Description: The reference
column name [bref] mapped on the
element [field bs] does not
correspond to a valid field on the
mapping reference.
By the way - if I remove the referenced-column-name="bref" from the definition I get the same exception for the referenced-column-name="id" on the inverse-join-column element. So I doubt that I have understood referenced-column-name correct. I used it to specify the database column name of the tables which are related to the join table. Is this correct?
SOLUTION:
The final error in my szenario was that I did not have the BREF field definied in my class
class A {
private long bref; // missing !
private List<B> bs;
}
and in my orm.xml mapping file for this class
<basic name="bref">
<column name="bref" />
</basic>
I was not aware that I have to define the used join mapping referenced-column-name attributes somewhere in my mapping classes (as I also did not have the join-table itself or the name attributes of join-column/inverse-join-column mapped to a class or class members.)
Also the tip to check the case issue was helpful for me. I feel now quite to verbose in specifying my mapping as I overwrite all default (uppercase) mappings with lowercase values. As my database is not case sensitive I will use upper case notation if special mapping is needed to go with the default.
+1 for all!
Can you try defining the field as "BREF" or the same exact case used if you defined it on the attribute mapping, or you can try setting the eclipselink.jpa.uppercase-column-names persistence property to true. This is likely the issue with "id" when referenced-column-name="bref" is removed, since it is likely the field in the entity defaults to "ID".
In general JPA requires that the foreign keys/join columns reference the primary key/Id of the Entity. But, this should work with EclipseLink, so please include the SQL that is being generated, and if it is wrong, please log a bug.
How is the Id of A defined, is it just ID or ID and BREF?
You can use a DescriptorCustomizer to customize the ManyToManyMapping for the relationship and set the correct foreign key field name.
I have two objects:
public class ParentObject {
// some basic bean info
}
public class ChildObject extends ParentObject {
// more bean info
}
Each of these tables corresponds to a differnet table in a database. I am using Hibernate to query the ChildObject, which will in turn populate the parent objects values.
I have defined my mapping file as so:
<hibernate-mapping>
<class name="ParentObject"
table="PARENT_OBJECT">
<id name="id"
column="parent"id">
<generator class="assigned"/>
</id>
<property name="beaninfo"/>
<!-- more properties -->
<joined-subclass name="ChildObject" table="CHILD_OBJECT">
<key column="CHILD_ID"/>
<!--properties again-->
</joined-subclass>
</class>
</hibernate-mapping>
I can use hibernate to query the two tables without issue.
I use
session.createQuery("from ChildObject as child ");
This is all basic hibernate stuff. However, the part which I am having issues with is that I need to apply locks to the all the tables in the query.
I can set the lock type for the child object by using the query.setLockType("child", LockMode.?). However, I cannot seem to find a way to place a lock on the parent table.
I am new to Hibernate, and am still working around a few mental roadblocks. The question is: how can I place a lock on the parent table?
I was wondering if there was a way around having to do this without undoing the Polymorphic structure that I have set up.
Why do you have to lock both tables? I'm asking because depending on what you're trying to do there may be alternative solutions to achieve what you want.
The way things are, Hibernate normally only locks the root table unless you're using some exotic database / dialect. So, chances are you're already locking your ParentObject table rather than ChildObject.
Update (based on comment):
Since you are using an exotic database :-) which doesn't support FOR UPDATE syntax, Hibernate is locking the "primary" tables as they are specified in query ("primary" in this case being table mapped for the entity listed in FROM clause, not the root of the hierarchy - e.g. ChildObject, not ParentObject). Since you want to lock both tables, I'd suggest you try one of the following:
Call session.lock() on entities after you've obtained them from the query. This should lock the root table of the hierarchy, however I'm not 100% sure on whether it'll work because technically you're trying to "upgrade" the lock that's already being held on a given entity.
Try to cheat by explicitly naming ParentObject table in your query and requesting lock mode for it:
String hql = "select c from ChildObject c, ParentObject p where c.id = p.id";
session.createQuery(hql)
.setLockMode("c", LockMode.READ)
.setLockMode("p", LockMode.READ).list();