I'm using Hibernate 3.2.2 GA with HSQLDB 2.0 GA, and I have a class hierarchy similar to the following:
#Entity
#Table(name = "A_TABLE")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorFormula(value = "case when CODE IN (1, 2, 3, 4) then 'TypeB'
when CODE IN (5, 6, 7, 8) then 'TypeC' else NULL end")
#org.hibernate.annotations.Entity(dynamicUpdate = true, dynamicInsert = true)
public abstract class A{
(...)
}
#Entity
#DiscriminatorValue("TypeB")
public class B extends A {
(...)
}
#Entity
#DiscriminatorValue("TypeC")
public class C extends A {
(...)
}
I'm trying to execute the following HQL query, which returns objects from both B and C classes.
String hql = "from A a where a.someAttr = 3";
Query query = session.createQuery(hql);
return query.list();
However, I get the following error:
org.hibernate.WrongClassException: Object with id: 2 was not of the specified subclass: A (Discriminator: C )
The strangest thing is that the object with id 2 is a C instance...
I've googled for this error and I've found some people who's faced it, none using InheritanceType.SINGLE_TABLE and DiscrimatorFormula, though. Has anyone run into this problem?
Make sure the entity is listed in your config file (persistence.xml, for example). From https://stackoverflow.com/a/14150629/116596
The problem is that you're getting a list of A's and Hibernate doesn't know enough to create B's or C's in a list based on the discriminator formula. Luckily, there's an annotation to deal with this.
Some people call this a bug, and I'm sort of inclined to agree. At any rate, the abstraction seems to be a bit leaky.
Add a #ForceDiscriminator annotation to the parent entity (A) and it's likely to work properly.
This solution is Hibernate-specific. I'm not sure if the problem extends to JPA in general or if there's a JPA solution.
EDIT:
This appears not to have done the trick.
It might be worthwhile to try to get the sql that hibernate is generating for you.
Add
<property name="hibernate.show.sql" value="true" />
to your hibernate config and see what happens.
Getting this sql with and without the force clause might give clues as to exactly what the force does and why it's not working.
I can't think of anything else at the moment, except the NULL in your discriminator formula looks a little risky.
Well, this got me curious: it may be you suffer from this issue which says:
The reason is the string is interpreted as a CHAR type rather than
VARCHAR. We may change this behaviour in the future.
Can you try to apply TRIM() on the result (inside the #DiscriminatorFormula) or test with another DBMS? This doesn't seem to be Hibernate specific.
I solved it using the DTYPE column and WHERE clause.
With your example, it would be:
#Entity
#WHERE(clause = "DTYPE = 'B'")
public class B extends A {
...
}
#Entity
#WHERE(clause = "DTYPE = 'C'")
public class C extends A {
...
}
Related
I am trying to write a Repository call in my microservices using JPA, now the problem is that the table name for which I am trying to make a repository call is a reserved keyword in SQL i.e. 'CASE'
Currently to overcome this problem we are writing a native query like this in our Repository
#Repository
public interface CaseRepository extends JpaRepository<Case, Long> {
#Query(value = "Select * from cft.\"case\" where id = :idCase", nativeQuery = true)
Case findCaseById(Long idCase);
}
Since this is not a good approach so I am trying to remove nativeQuery and trying to do something like this
#Repository
public interface CaseRepository extends JpaRepository<Case, Long> {
#Query("select c from Case c where c.id = :idCase")
Cases getCaseById(Long idCase);
}
But this block of code is giving an error because Case is a reserved keyword. For this I tried to look for issues similar to this here in stackoverflow but the suggestion which I saw was that the name of Table needs to be changed but since in our case this table is being used in lot of places so can't take risk of changing the name.
Any hints regarding how we can overcome this issue are appreciated.
you have to use double quotes in the name of the table so it takes the name literally. In your entity class you should use something like:
#Entity
#Table(name = "\"case\"")
public class Case {
}
Imagine the following scenario where we use inheritance strategy TABLE_PER_CLASS and Template as superclass while Product as subclass.
Template:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "Templates")
#NamedQuery(name = "getAllTemplates", query = "SELECT t FROM Template t")
public class Template implements Serializable { ...}
Product:
#Entity
#Table(name = "Product")
public class Product extends Template implements Serializable { ... }
In this scenario even thought i have in my DB 2 templates and 1 product. Whenever i call the Template namedQuery i retrieve both Products and Templates alike.
I tried do something like so:
SELECT t FROM Template t WHERE TYPE(t) = Template
However it returns the following error:
The class does not have a descriptor, or a descriptor that does not use inheritance or uses a ClassExtractor for inheritance
Is there a way to only get the Templates?
The TYPE operator does not work for sub classes either when using TABLE_PER_CLASS. There seems not to be explicit information about using TYPE with TABLE_PER_CLASS.
However there are lots of posts saying that this strategy is inefficient and not exactly recommended to be used.
JPA 2.1 specifications say about TABLE_PER_CLASS:
Support for the TABLE_PER_CLASS mapping strategy is optional in this release.
This means also that support might be only partial: like TYPE support not implemented.
Also there are some posts that indicate that not only Hibernate suffers from this, see (some pretty old, but still), like: this and this.
So as a conclusion:
change to SINGLE_TABLE or JOINED strategy if possible
live with it
In Hibernate you could also try to get it working with #DiscriminatorColumn and adding the column to Template entity but I personally think it is not worth the effort.
I have 2 Entitites, one maps to a database table, the other to a database view.
The data of the view depends on the table.
#javax.persistence.Table(name = "BOOKING_INFO", schema = "BOOKING")
#Entity
public class BookingInfo extends AbstractBooking {
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
public class BookingView extends AbstractBooking {
This works fine in most cases, however when we write (insert or update) the Booking entity and then do queries (in my case a count) on the BookingView entity, we get stale data.
Why this happens is clear to me (hibernate caching, it only flushes when it detects that a select needs some data flushed).
So if I would do a query on the Booking entity, it would trigger a flush.
I have found the #Synchronize Annotation in Hibernate which sounds like it should fix this problem, like this:
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
#Synchronize("BOOKING.BOOKING_INFO")
public class BookingView extends AbstractBooking {
However this does not make any difference (flush only happens at the end of the transaction). Also the documentation I have found about this annotation is quite lacking and not very helpful.
EDIT: I also tried #Synchronize("BOOKING_INFO") (without the schema name, and also lowercase, but that made no difference)
The docs say that it is mostly used with #Subselect but it is not a must (I don't want that).
Has anyone ever successfully used this annotation?
Is there any other way to handle database views in Hibernate?
Am I missing something else?
Thanks to a colleague we were able to debug and fix this, the problem was that our Hibernate naming-strategy lowercased our table-names, so the correct annotaiton is:
#Synchronize("BOOKING.booking_info")
How to debug this:
set breakpoints in Hibernates ActionQueue class in the areTablesToBeUpdated methods.
There we saw that it compared "BOOKING.BOOKING_VIEW" to "BOOKING.booking_view".
We think this is a bug in hibernate because it should either apply the naming-strategies also to the values from #Synchronize or compare these case-insensitive (which could theoretically lead to too many flushes if you have a crazy database which uses tables with the same name only differentiated by casing).
Created a Hibernate issue: https://hibernate.atlassian.net/browse/HHH-10002
I have a sample code piece like this-
#Entity
#Table(name = "employee")
#Where(clause = "active IS TRUE")
public class Employee{
}
This will fetch all the record of employee table having active=true or 1. In some cases, it may require that I want to load the records having active=false or 0.
If I write my code as FROM Employee emp WHERE emp.active IS FALSE but the generated query contains bot the conditions given in HQL and Annotations.
Hence, the expected results is not coming. Is there anyway to override this predefined #Where defined over entity?
I know its too old question but I was facing same issue and thought I should share my workaround.
Totally agree with #cнŝdk answer as you cannot override but you can ignore #Where clause by defining nativeQuery as below:
#Query(value = "Select * from customer where company_id = ?1", nativeQuery = true) List<Customer> findByCompanyIdIgnoringEntityWhere(Long companyId);
The SQL in the #Query annotation must point the table's name and the fields' names (not entity's name).
AFAIK you can't override it inside your class because if you take a look at the #Where documentation you will see that this interface is annotated #Retention(value=RUNTIME) so it has RUNTIME as a RetentionPolicy and you can see in the RetentionPolicy documentation that:
RUNTIME:
Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
Which force the #Where annotation to be recorded in the class file by the compiler and retained by the VM at run time, so it will be applied all over this class.
I'm observing what I think is an unexpected behaviour in JPA 2 when fetching a list attribute with a criteria query.
My query is as follows (an extract of it):
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<MainObject> c = b.createQuery(MainObject.class);
Root<MainObject> root = c.from(MainObject.class);
Join<MainObject, FirstFetch> firstFetch = (Join<MainObject, FirstFetch>) root.fetch(MainObject_.firstFetch);
firstFetch.fetch(FirstFetch_.secondFetch); //secondFetch is a list
c.select(root).distinct(true);
(So let's say I'm fetching a list as a property of the property of an object.)
The thing is when the query returns multiple results, secondFetch values are duplicated as many times as rows are returned. Each firstFetch should have just one secondFetch but has n instead.
The only particularity i see in this case is all MainObjects happen to have the same FirstFetch instance.
So my guess is the join is being crossed, which is normal, but then JPA fails to assign its secondFetch object to each one of the firstFetchs.
Mappings shouldn't be too special, the're more or less like this
#Entity
#Table(name="mainobject")
public class MainObject{
//...
private FirstFetch firstFetch;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="mainObject_column")
public FirstFetch getFirstFetch() {
return firstFetch;
}
}
and
#Entity
#Table(name="firstFetch")
public class FirstFetch{
//...
private List<SecondFetch> secondFetch;
#OneToMany(mappedBy="secondFetch")
public List<SecondFetch> getSecondFetch() {
return secondFetch;
}
}
& finally
#Entity
#Table(name="secondFetch")
public class SecondFetch {
//....
private FirstFetch firstFetch; //bidirectional
#ManyToOne
#JoinColumn(name="column")
public FirstFetch getFirstFetch() {
return firstFetch;
}
}
I've been looking for some sort of distinct sentence to apply to the fetch but there's none (would have been a 'patch' anyway...)
If i change
List<SecondFetch>
for
Set<SecondFetch>
i'll get the expected result thanks to Sets' Keys, so I do feel this is kind of a misbehaviour in JPA's lists.
I'm not an expert, though, so i could perfectlly be making some mistake in the mappings or query.
Any feeback is very welcome to help clear this out.
Thanks.
I had the exact same problem though I was using JPA criteria API to do the query.
After some research I found a solution which you already mentioned (but was not available, since your not using criteria API): Using distinct.
With JPA criteria it would look like this:
CriteriaQuery<FirstFetch> query = cb.createQuery(FirstFetch.class);
Root<AbschnittC> root = query.from(FirstFetch.class);
root.fetch(FirstFetch_.secondFetch, JoinType.LEFT);
query.distinct(true);
Without using query.distinct(true); the resultset was multiplied with the amount of objects in the secondFetch list.
Hibernate does have something like DISTINCT_ROOT_ENTITY which sound more adequate than just setting a query distinct. But I have not further investigated this. I am also using Hibernate as the JPA provider. Maybe setting the query distinct in JPA ends up using the same code as Hibernates DISTINCT_ROOT_ENTITY would?