4

I have an scenario as the following:

@Entity
@Table(name = "ANIMAL")
@Inheritance(strategy = InheritanceType.JOINED)

public class Animal implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "S_ANIMAL")
    @SequenceGenerator(name = "S_ANIMAL", sequenceName = "S_ANIMAL", allocationSize = 1)
    public int getNumero() {
        return numero;
    }

    public void setNumero(int numero) {
        this.numero = numero;
    }
        .
        .
        .
}

and as the subclass:

@Entity
@Table(name = "DOG")
public class Dog extends Animal {

    private static final long serialVersionUID = -7341592543130659641L;
        .
        .
        .
}

I have a JPA Select statement like this:

SELECT a FROM Animal a;

I'm using Hibernate 3.3.1

As I can see the framework retrieves instances of Animal and also of Dog using a left outer join.

Is there a way to Select only the "part" Animal? I mean, the previous Select will get all the Animals, those that are only Animals but not Dogs and those that are Dogs.

I want them all, but in the case of Dogs I want to only retrieve the "Animal part" of them.

I found the @org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT) but as I could see this only works if Animal isn't an @Entity.

Thanks a lot.

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
Gustavo Fava
  • 63
  • 1
  • 4

3 Answers3

3

Short answer: The behaviour you describe complies with JPA standard. There is no JPA standard way to restrict the JPA provider to retrieve only the superclass.

The provider can choose query implementation logic to achieve functionality, consistency & performance. As long as it respects your entity annotations and returns the requested information in the query all is good. Think of the outer join to Dog as private implementation detail that should not concern you. The provider has coded an outer join to assist performance and consistency.

Consider:

  • JPA is defined to work on java object Entities not tables
  • Root of your Entity hierarchy, Animal, is not abstract, so you can create an instance and persist it.
  • You have a default value for Animal @DescriminatorColumn and @DescriminatorType - so Animal table will have a discriminator column added with name "DTYPE" and type "some String type". You have a default value for @DescriminatorValue - will be equal to the Entity name: Animal. So Animal.DTYPE column = "Animal" when you create this entity.
  • The Entity Dog has a superclass Animal. The default value is also used for its @DescriminatorValue - Dog. So Animal.DTYPE column = "Dog" when you create this entity.
  • Java class restrictions ensure that whenever a Dog object exists a corresponding Animal superclass object also exists.
  • Very often, when you have an Entity Animal loaded into JPA persistence context, with a @DescriminatorValue (value stored in DTYPE column) = "Dog", then it is very useful to have Dog object loaded into PC for consistency. Even though it is not required by the JPA standard.
  • The inheritance relationship cannot be specified to be EAGER or LAZY (like a basic type field or entity relationship field). If you needed to read/update dog properties and the Dog class was not loaded what would you do? Run a separate query to reload it? This would hurt consistency and performance greatly.
  • The main concern for performance is the total number of separate SQL commands sent to the DB.
    In terms of time taken: query with animal table is only slightly faster (10s of microseconds???) than query with animal outer joined to dog, but is much faster than two separate queries (one for animal and one for dog)
Glen Best
  • 22,769
  • 3
  • 58
  • 74
  • Hi Luiggi, thanks for your answer. It is very usefull for me cause you helped me to order many things that were in my mind but in a messed way. Now i have the things in order. The problem that arises from the JPA behavior is that when you have an @Entity that has an attribute (for example in my case is of type jts.Geometry), that the DB (in my case Oracle) implements as an Object which some of its attributes are of type VARRAY, when you try a SELECT DISTINCT you get the ORA-22901. Let's say in my case Dog has an attribute of type Goemetry. ORACLE uses the object type SDO_GEOMETRY for it. – Gustavo Fava Nov 05 '12 at 16:27
  • When SELECT DISTINCT a FROM Animal, JPA retrieves Animals and Dogs so the sql that goes to the DB has select distinct a, b, c ..... and at the DB level b is an "oracle object type" that containts 2 attributes of type VARRAY, and Oracle rises the ORA-22901. I couldn't find yet a workaround for this. Regards, Gustavo. – Gustavo Fava Nov 05 '12 at 16:31
  • I'm sorry, in my comment i wrote "Hi Luiggi" but i was trying to thank to Glen Best for his answer. I'm new in posting at stackoverflow and i made thie mistake. I appology Glen for my mistake. Regards, Gustavo. – Gustavo Fava Nov 06 '12 at 17:22
  • @GlenBest We are facing similar problem. For us it is absolutely essential that only superclass be fetched because we have 3 subclasses and also lots of eagerly fetched collections. Is there any new solution to this problem? – coolscitist Mar 10 '15 at 14:19
  • 'Is there any new solution to this problem?' I don't think there is any problem here. I don't think there will be any solution. Need to understand the idea of inheritance. Superclass+subclass always exist together. If you have object A that can exist independently of object B - then this is not an ancestor relationship. :) – Glen Best Sep 11 '15 at 05:03
0

Actually, there is a way to get just the superclass: you just need to use the native query from JPA. In my case, I am using JPA Repositories. Therefore, it would be something like that:

@Query(value = "SELECT * FROM animal", nativeQuery = true)
List<Resource> findAllAnimal();

The flag nativeQuery as true allow running the native SQL on database.

If you are using Entity Manager then check this out: https://www.thoughts-on-java.org/jpa-native-queries/

julianfperez
  • 1,726
  • 5
  • 38
  • 69
0

Hibernate uses a property "clazz_" to determine subclasses.

Example:

select 
  animal0_.animal_id as animal_i1_0_,..., 
  case 
    when animal0_1_.animal_id is not null then 1 
    when animal0_2_.animal_id is not null then 2 
    when animal0_.animal_id is not null then 0 
  end as clazz_
from animal animal0_ 
left outer join dog animal0_1_ on animal0_.animal_id=animal0_1_.animal_id
left outer join cat animal0_2_ on animal0_.animal_id=animal0_2_.animal_id

You should specify SQL with "0 AS clazz_"

@Query(value = "SELECT *, 0 AS clazz_ FROM animal", nativeQuery = true)
List<Animal> findAllAnimal();