It's possible to use queryDSL generated classes to refer abstract methods from class?
Here is a example:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
class A {
#Id private Long id;
public getId/setId;
protected abstract Date finalDate();
}
#Entity
class B extends A {
private Date finalDate;
public getFinalDate/setFinalDate;
}
#Entity
class C extends A {
private B b;
public getFinalDate(){return b.getFinalDate());
}
I would like to use a query like this:
new JPAQuery<A>(em)
.select(a)
.where(a.finalDate.isNotNull())
.fetch();
But after build phase (Construct meta classes), the generated A class (QA.class), doesn't has nothing like.
JB Nizets comment is the answer:
No the query needs to be translated to SQL, and executed by the
database. The database doesn't know and care about your classes and
methods. All it knows about is its tables and columns. – JB Nizet 10
hours ago
Related
I have a project that I've recently inherited that has 2 tables that share a lot of common fields. I'm new to hibernate and want to know if I can use composition to generate the table instead of inheritance? B and D are basically the same class with a different table name.
Current Hierarchy is
B extends A extends BaseClass
D extends C extends BaseClass
My problem at the moment is that a lot of other classes extend BaseClass which don't have the shared fields and the 2 child classes don't share a common parent so I cannot add another level into the Hierarchy and use #MappedSuperclass.
Because of this I'd like to know if I can group my common fields into a single class and compose my child classes with this new class somehow?
Apologies for the cryptic names but as always; confidentiality...
edit - found something simmilar with #Embeddable https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/domain/embeddables.html
If you are using the JPA interface to Hibernate, you can use #Embedded and #Embeddable, to get more or less what you want. Be aware that the change will not be transparent: where you had:
#Entity
public class B extends A {
#Basic
private int foo;
...
}
that you referenced in JPQL by using b.foo, you will have:
#Embeddable
public classs Common {
#Basic
private int foo;
...
}
#Entity
public class B extends A {
#Embedded
private Common common;
...
}
which you will have to reference in JPQL using b.common.foo.
Read more about embeddable entities here:
https://en.wikibooks.org/wiki/Java_Persistence/Embeddables
You could potentially use #Embedded and embed the same object for both B and D, perhaps something like:
#Embeddable
public class CommonFieldObject {
#Column(name="COMFIELD1")
private String commonField1;
#Column(name="COMFIELD2")
private String commonField2;
...
}
#Table
public class C extends A {
#Embedded
#AttributeOverrides({
#AttributeOverride(name="commonField1", column=#Column(name="CFO_COMFIELD1")),
#AttributeOverride(name="commonField2", column=#Column(name="CFO_COMFIELD2"))
})
private CommonFieldObject commonFieldObj; //CFO_ prefix for this reference - in case we have a second field referencing a CommonFieldObject - use a different prefix..
...
}
You should then get the columns CFO_COMFIELD1 and CFO_COMFIELD2 in your table and you can recycle the CommonFieldObject for class D.
I have two entities
#Entity
#Table(name = "view_a")
public class A extends BaseStringIdTableClass
#Entity
#Table(name = "view_b")
public class B extends BaseStringIdTableClass
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseStringIdTableClass implements Serializable {
#Id
private String id;
And in the database i have two views
select * from view_a
|ID|ColumnA|....
|34222|some Value|....
select * from view_b
|ID|ColumnB|...
|34222|lla lla|...
I have therefore in the database different views. But the rows in this different views have the same ID.
Now i try to read the entities with the standard CRUD Repository.
A a = aRepository.findById("34222").get();
B b = bRepository.findById("34222").get();
In this case i can not find entity b. If i swop the two lines of code i can not find entity a.
I think the persistence context can at one time containt for a specific ID only one entity? Is this right. What can i do?
Repository definitions
public interface ARepository extends JpaRepository<A, String>, QuerydslPredicateExecutor<A> {
public interface BRepository extends JpaRepository<B, String>, QuerydslPredicateExecutor<B> {
Sleeping over one night always help....First sorry for my initial incomplete question.
The problem/error was the both entities extended the same abstract class. And in this abstract class the ID was definied.
Fix after this recogniton was easy. One of the entities does not extend my abstract class, but definies his own id. And now it works......
I can create a repository via defining an interface on the appropriate JPA class A like the following:
public interface ARepository extends CrudRepository<A, Long>
{
}
and I can use that in my Controller (for example) via
#Autowired
private ARepository aRepository;
and just can do things like this:
aRepository.save(..);
aRepository.findAll();
..
No problem so far.
But my problem is that I have ca. 500 JPA classes and need to access each table which means to define 500 Repositories in the style of above.
So does exist an thing to create that either dynamically via some Spring Data "magic" which from my point of view should exist otherwise the above would not be possible. It looks like this is similar to my problem.
Apart from that one more issue related to the above. I can define findBy... methods in the interface and in the background there will be generated a query method for this particular attribute. The question is also if this can be done in a dynamic way related to the previous question, cause I have groups of tables which need supplemental query methods..
There is spring-data-generator which can automatically generate the interfaces for you.
Regarding your 2nd question I don't think you that can be done in a dynamic way. Java is statically compiled and there's no way to add members dynamically. There could be a tool that generates code for those methods but if that tool generates methods for all combinations of columns you will end up with a huge amount of methods.
You can make a base abstract entity for your 500 classes an then create one repo for this class. (I think it's a common practice to have a BaseEntity class with id, version etc. for every entity in the project).
For simple repo methods (like save, findAll etc.) it will work right from the box (note - entities must have the equal id type). For example:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstarct class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
}
#Entity
public class Entity1 extends BaseEntity {
private String name;
}
#Entity
public class Entity2 extends BaseEntity {
private String name;
}
public interface BaseEntityRepo extends JpaRepository<BaseEntity, Long> {
}
Note that BaseEntity must have #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) to prevent of using singe table base_entity for every entity. And their ids must not intersect (see #GeneratedValue(strategy = GenerationType.SEQUENCE)).
Usage:
#RunWith(SpringRunner.class)
#SpringBootTest
public class BaseEntityRepoTest {
#Autowired private BaseEntityRepo repo;
#Before
public void setUp() throws Exception {
repo.save(asList(
new Entity1("entity1"),
new Entity2("entity2")
));
}
#Test
public void readingTest() throws Exception {
List<BaseEntity> entities = repo.findAll();
assertThat(entities).hasSize(2);
}
}
Related to your second question you can use this approach:
public interface BaseEntityRepo extends JpaRepository<BaseEntity, Long> {
<T> T findById(Long id, Class<T> type);
}
Usage:
#Test
public void findById() {
final Entity1 entity1 = repo.findById(1L, Entity1.class);
final Entity2 entity2 = repo.findById(2L, Entity2.class);
assertThat(entity1).isNotNull();
assertThat(entity2).isNotNull();
}
But you can build repo query methods only for 'common' properties of inherited entities which are present in the base class. To make this method work you must move the name parameter to the BaseEntity:
<T> List<T> findAllByNameLike(String name, Class<T> type);
I am using Hibernate to connect to my database.
I have an inheritance structure in my application.The problem is that when i do a query like "from Animal", it does a left outer join for the class Animal,its sub classes and all the associations for Animal and its subclasses.
How do i avoid this situation.I want to load the data only when i specify it through a fetchmode in my criteria query?
Yes, Hibernate supports polymorphic queries. From the documentation:
14.8. Polymorphic queries
A query like:
from Cat as cat
returns instances not only of Cat, but
also of subclasses like DomesticCat.
Hibernate queries can name any Java
class or interface in the from clause.
The query will return instances of all
persistent classes that extend that
class or implement the interface. The
following query would return all
persistent objects:
from java.lang.Object o
The interface Named might be
implemented by various persistent
classes:
from Named n, Named m where n.name = m.name
These last two queries will require
more than one SQL SELECT. This means
that the order by clause does not
correctly order the whole result set.
It also means you cannot call these
queries using Query.scroll().
This is the default behavior (called implicit polymorphism) and Hibernate supports both implicit and explicit polymorphism:
Implicit polymorphism means that instances of the class will be
returned by a query that names any
superclass or implemented interface or
class, and that instances of any
subclass of the class will be returned
by a query that names the class
itself. Explicit polymorphism means
that class instances will be returned
only by queries that explicitly name
that class. Queries that name the
class will return only instances of
subclasses mapped inside this
<class> declaration as a
<subclass> or <joined-subclass>.
For most purposes, the default
polymorphism="implicit" is
appropriate. Explicit polymorphism is
useful when two different classes are
mapped to the same table This allows a
"lightweight" class that contains a
subset of the table columns.
This can be configured at the class level. Use polymorphism="explicit" if you are if you are using xml mappings, see 5.1.3 Class. Use Hibernate's #Entity annotation if you're using annotations, see 2.4.1. Entity. Below an example:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)
#Inheritance(strategy = InheritanceType.JOINED)
public class Foo {
...
}
Assume you have a class structure as follows:
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
then when you select all Animals, you'd expect to also load all Dogs and Cats. After all they are Animals.
A different story are the associations. You can created you mappings such that the associations are lazy load instead of eager load.
Basically it's the default ORM inheritance design pattern used by Hibernate called class inheritance (all the classes are mapped to a single table), if you want to change that you can google:
- single class hierarhy or table per class (this will map every class to a separate table in the DB)
- concrete class hierarhy (this will map only the concrete implementations to a table).
To avoid multiple joins during class hierarchy fetching you can apply SINGLE_TABLE hierarchy mapping strategy, and then define secondary tables on subclasses with SELECT fetching strategy. However, this turns you "heavy join" model into "N+1 select" model. The example:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = Super.DISCRIMINATOR_COLUMN, discriminatorType = DiscriminatorType .STRING, length = 255)
public class Super {
public static final String DISCRIMINATOR_COLUMN = "classname";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
#Column(updatable = false, insertable = false)
protected String classname;
protected String superProp = "superProp";
public long getId() {
return id;
}
public String getClassname() {
return classname;
}
public String getSuperProp() {
return superProp;
}
public void setSuperProp(String superProp) {
this.superProp = superProp;
}
}
#Entity
#SecondaryTable(name = SubA.TABLE)
#Table(appliesTo = SubA.TABLE, fetch = FetchMode.SELECT)
public class SubA extends Super {
public static final String TABLE = "SUBA";
#Column(table = TABLE)
protected String subAProp = "subAProp";
public String getSubAProp() {
return subAProp;
}
public void setSubAProp(String subAProp) {
this.subAProp = subAProp;
}
}
#Entity
#SecondaryTable(name = SubB.TABLE)
#Table(appliesTo = SubB.TABLE, fetch = FetchMode.SELECT)
public class SubB extends Super {
public static final String TABLE = "SUBB";
#Column(table = TABLE)
protected String subBProp = "subBProp";
public String getSubBProp() {
return subBProp;
}
public void setSubBProp(String subBProp) {
this.subBProp = subBProp;
}
}
And what SQL is done on from Super HQL query:
select [...] from SUPER super0_
select super_1_.subaprop as subaprop1_83_ from SUBA super_1_ where super_1_.id=1
select super_2_.subbprop as subbprop1_84_ from SUBB super_2_ where super_2_.id=2
More about this approach and general hibernate performance hints you can read in my article.
Suppose a Table per subclass inheritance relationship which can be described bellow (From wikibooks.org - see here)
Notice Parent class is not abstract
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Project {
#Id
private long id;
// Other properties
}
#Entity
#Table(name="LARGEPROJECT")
public class LargeProject extends Project {
private BigDecimal budget;
}
#Entity
#Table(name="SMALLPROJECT")
public class SmallProject extends Project {
}
I have a scenario where i just need to retrieve the Parent class. Because of performance issues, What should i do to run a HQL query in order to retrieve the Parent class and just the Parent class without loading any subclass ???
A workaround is described below:
Define your Parent class as MappedSuperClass. Let's suppose the parent class is mapped To PARENT_TABLE
#MappedSuperClass
public abstract class AbstractParent implements Serializable {
#Id
#GeneratedValue
private long id;
#Column(table="PARENT_TABLE")
private String someProperty;
// getter's and setter's
}
For each subclass, extend the AbstractParent class and define its SecondaryTable. Any persistent field defined in the parent class will be mapped to the table defined by SecondaryTable. And finally, use AttributeOverrides if needed
#Entity
#SecondaryTable("PARENT_TABLE")
public class Child extends AbstractParent {
private String childField;
public String getChildProperty() {
return childField;
}
}
And define another Entity with the purpose of retrieving just the parent class
#Entity
#Table(name="PARENT_TABLE")
#AttributeOverrides({
#AttributeOverride(name="someProperty", column=#Column(name="someProperty"))
})
public class Parent extends AbstractParent {}
Nothing else. See as shown above i have used just JPA specific annotations
Update: It appears the first option doesn't work as I thought.
First option:
Specify the class in the where clause:
select p from Project p where p.class = Project
Second option:
Use explicit polymorphism that you can set using Hibernate's #Entity annotation:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(polymorphism = PolymorphismType.EXPLICIT)
#Inheritance(strategy = InheritanceType.JOINED)
public class Project {
#Id
private long id;
...
}
This is what Hibernate Core documentation writes about explicit polymorphism:
Implicit polymorphism means that
instances of the class will be
returned by a query that names any
superclass or implemented interface or
class, and that instances of any
subclass of the class will be returned
by a query that names the class
itself. Explicit polymorphism means
that class instances will be returned
only by queries that explicitly name
that class.
See also
How to get only super class in table-per-subclass strategy?
Actually, there is a way to get just the superclass, you just need to use the native query from JPA, in my case I'm using JPA Repositories it would be something like that:
#Query(value = "SELECT * FROM project", nativeQuery = true)
List<Resource> findAllProject();
The flag nativeQuery as true allow running the native SQL on database.
If you are using Entity Manager check this out: https://www.thoughts-on-java.org/jpa-native-queries/