Accessing #MappedSuperclass' properties in Criteria Query using metamodel classes - jpa

I'm trying to access a property defined in #MappedSuperclass in a Criteria Query with JPA meta-model (Hibernate 4.2.8):
#MappedSuperclass
public class BaseEntity {
private DateTime createdOn;
}
My Entity:
#Entity
#Table(name = "HISTORY")
public class History extends BaseEntity implements Serializable {
private Long id;
}
How to access createdOn?
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<History> c = cb.createQuery(History.class);
Root<History> h = c.from(History.class);
h.orderBy(cb.asc(a.get(History_.createdOn)));
History_:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(History.class)
public abstract class History_ extends com.test.BaseEntity_ {
public static volatile SingularAttribute<History, Long> id;
}
Is there a way to access base class' properties?

Of course there is a way. As I see in your code, you already access it: History_.createdOn is a property of the MappedSuperclass. If by "to access" you mean to select, than you simply do:
query.select(History_.createdOn)

Related

JPQL query on abstract mother class accessing subclasses properties

I'm facing a problem trying to make a JPA repository on an abstract class.
What I'm willing to do is a method that filters, paginates and returnes 3 differents kind of objects in a single list. Some have shared properties (grouped in AbstractClass).
Here are my different classes :
Mother abstract class
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "te_abstract_data")
#DiscriminatorColumn(name="data_type")
public abstract class AbstractData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "data_type")
#Enumerated(EnumType.STRING)
private DataType dataType;
}
AbstractClass
#MappedSuperclass
public class AbstractClass extends AbstractData {
#ManyToOne
#JoinColumn(name = "fk_obj_id")
private ObjEntity opbj;
}
ClassA & ClassB classes
#Entity
#DiscriminatorValue("CLASS_A")
#Table(name = "te_class_a")
#PrimaryKeyJoinColumn(name="fk_data_id")
public class ClassAEntity extends AbstractClass {
// some fields...
}
ClassC
#Entity
#DiscriminatorValue("CLASS_C")
#Table(name = "te_class_c")
#PrimaryKeyJoinColumn(name="fk_data_id")
public class ClassCEntity extends AbstractData {
// some fields...
}
And here is my repository :
#Repository
public interface DataDao extends JpaRepository<AbstractData, Integer> {
#Query(value =
"SELECT ad " +
"FROM AbstractData ad " +
"WHERE ad.obj.name = :objName " +
// some other filters on various fields
"ORDER BY ad.id ASC"
)
List<AbstractData> findFiltered(
Pageable pageable,
String objName
);
}
The current query in my repository ignores only returns ClassB object, even if ClassA and C's objects are well initialized. I have no idea about what could be wrong...
Any idea how I could solve this ?

Using #MappedSuperclass in spring repository #Query

I am trying to define common query for all repositories extended my base repository:
#NoRepositoryBean
public interface DocumentRepository<T extends BaseDocument> extends JpaRepository<T, Long> {
#Query(value = "FROM BaseDocument bd WHERE (bd.createdAt IS NULL OR :to IS NULL OR bd.createdAt < :to) AND (bd.deletedAt IS NULL OR :from IS NULL OR bd.deletedAt > :from)")
Iterable<T> findAllActiveBetween(OffsetDateTime from, OffsetDateTime to);
}
#MappedSuperclass
#Where(clause="deleted_at IS NULL")
abstract public class BaseDocument {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private OffsetDateTime createdAt;
private OffsetDateTime deletedAt;
}
The problematic part is using BaseDocument in #Query.
It is posible to define such method without duplication in all sub-repositories?
When you use #MappedSuperclass the inherited classes do not share a table to select from, so each of them needs a separate query. Inheritance of the repository cannot help you here.
If you use another inheritance strategy, for example a joined multiple table inheritance it would be possible to select from the table of the superclass and then you can use the java objects to navigate. If you need to use properties from the subclass in the query, each subclass would need to implement their own queries of course.
#NoRepositoryBean
public interface DocumentRepository<T extends BaseDocument> extends JpaRepository<T, Long> {
#Query(value = "FROM BaseDocument bd WHERE (bd.createdAt IS NULL OR :to IS NULL OR bd.createdAt < :to) AND (bd.deletedAt IS NULL OR :from IS NULL OR bd.deletedAt > :from)")
Iterable<T> findAllActiveBetween(OffsetDateTime from, OffsetDateTime to);
}
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="BaseDocument_TYPE")
#Table(name="BaseDocument")
abstract public class BaseDocument {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private OffsetDateTime createdAt;
private OffsetDateTime deletedAt;
}
#Entity
#DiscriminatorValue("TextDocument")
#Table(name="TextDocument")
public class TextDocument extends BaseDocument {
private String text;
}
#Entity
#DiscriminatorValue("AudioDocument")
#Table(name="AudioDocument")
public class AudioDocument extends BaseDocument {
private byte [] audio;
}

Build method in JPA with composite key

This is my entity with composite key:
#Entity
#Table(name = "course_ratings")
public class CourseRating {
#EmbeddedId
private CourseRatingKey id;
}
Where CourseRatingKey looks like this:
#Embeddable
public class CourseRatingKey implements Serializable {
#Column(name = "user_id")
private int userId;
#Column(name = "course_id")
private int courseId;
public CourseRatingKey() {
}
// getters, setters, equals(), hashCode()
}
And my JPA repository
#Repository
public interface CourseRatingRepository extends JpaRepository<CourseRating, CourseRatingKey> {
}
I am trying to build method that will return list of all CourseRating with given courseId property of CourseRatingKey. Below method doesn't work because JPA doesn't recognize it:
repository.findAllByIdCourseId(int id);
How can I build my method name to achieve my goal?
I have solved my problem by declaring this method in repository. I'm a bit confused as the other methods work without declaring.
#Repository
public interface CourseRatingRepository extends JpaRepository<CourseRating, CourseRatingKey> {
List<CourseRating> findAllByIdCourseId(Integer id);
}

JPA static metamodel for multiple persistence units

JPA: for the generated static metamodel, for example,
#Entity
public class Foo {
private Long id;
}
#StaticMetamodel(...)
public class Foo_ {
public static volatile SingleAttribute<Foo, Long> id;
}
If the entity class Foo is in multiple persistence units(each persistence unit has a Metamodel), each Metamodel has a different ManagedType instance for the Foo class. which metamodel does the static attributes (e.g. id in the example) belong? Each attribute has method getDeclaringType() which returns ManagedType.

How to declare a base repository for a MappedSuperclass base type with spring data?

We're having a hierarchy of entity classes where all inherit from a BaseEntity #MappedSuperclass. Is there a way to have a spring-data "base repository" that allows you to query for any BaseEntity without having to know it's type and use a specific repository?
#MappedSuperclass
public class BaseEntity {
#Id
private Long id;
private String owner;
// setter/getter etc...
}
#Entity
public class FooEntity extends BaseEntity {
private String foo;
}
#Entity
public class BarEntity extends BaseEntity {
private String bar;
}
/**
* How to declare this base repository....
*/
public interface BaseRepository extends JpaRepository<BaseEntity, ActionPK> {
List<BaseEntity> findByOwner(String owner);
}
#Component
public class MyService {
#Autowired
private BaseRepository baseRepository;
public void doSomething() {
// ... so that is can be used like this:
List<BaseEntity> entities = baseRepository.findByOwner("john");
doSomethingWith(entities);
}
}
I think the only way you could achieve this is to create a Service class that will orchestrate all the calls to your Repository classes that have an owner property. That service call would return your list of BaseEntity objects.