Hibernate Criteria. Construct with inheritance - java

I have super class AuditEntity.
#Getter
#Setter
#MappedSuperclass
#EntityListeners(AuditEntityListener.class)
public abstract class AuditEntity {
public static final String ID = "id";
public static final String ORGANIZATION = "organization";
public static final String CREATED_BY = "createdBy";
public static final String MODIFIED_BY = "modifiedBy";
public static final String CREATED_DATETIME = "createdDatetime";
public static final String MODIFIED_DATETIME = "modifiedDatetime";
public static final String STATE = "state";
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "organization_id")
protected Organization organization;
#Column(name = "created_datetime")
protected Instant createdDatetime;
#Column(name = "modified_datetime")
protected Instant modifiedDatetime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "created_by")
protected User createdBy;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "modified_by")
protected User modifiedBy;
#Enumerated(EnumType.STRING)
#Column(name = "state")
protected State state;
}
And entity extends the superclass.
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(of = {"id"}, callSuper = false)
#Entity
#Table(name = "inventory_request")
public class InventoryRequest extends AuditEntity {
public static final String NAME = "name";
public static final String REQUESTER = "requester";
public static final String SOURCE = "source";
public static final String EVENT = "event";
public static final String TRANSFER = "transfers";
public static final String ASSIGNMENT = "assignment";
public static final String INVENTORY_REQUEST_STATUS = "status";
public static final String NOTES = "notes";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "inventory_request_id")
private UUID id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "email", nullable = false)
private String email;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "requester_id")
private User requester;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "source_id")
#ApiModelProperty(hidden = true)
private User source;
#Enumerated(EnumType.STRING)
#Column(name = "status", nullable = false, length = 24)
private InventoryRequestStatus status;
#Column(name = "carrier")
private String carrier;
#Column(name = "tracking_number")
private String trackingNumber;
#Column(name = "note", length = 1024)
private String note;
#JsonManagedReference
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "event_id")
private Event event;
#Column(name = "number", nullable = false)
private Long number;
#Column(name = "tracking_enabled", nullable = false)
private Boolean trackingEnabled;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = Transfer.INVENTORY_REQUEST)
private Set<Transfer> transfers;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = InventoryRequestAssignment.INVENTORY_REQUEST)
private Set<InventoryRequestAssignment> assignment;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = InventoryRequestNote.INVENTORY_REQUEST)
private Set<InventoryRequestNote> notes;
}
This is a class i want to select with criteria api.
#NoArgsConstructor
#Getter
#Setter
public class InventoryRequestDTO extends InventoryRequest {
public InventoryRequestDTO(InventoryRequest inventoryRequest,
Long completeSetsQuantity,
Long specificProductQuantity) {
super(inventoryRequest.getId(),
inventoryRequest.getName(),
inventoryRequest.getEmail(),
inventoryRequest.getRequester(),
inventoryRequest.getSource(),
inventoryRequest.getStatus(),
inventoryRequest.getCarrier(),
inventoryRequest.getTrackingNumber(),
inventoryRequest.getNote(),
inventoryRequest.getEvent(),
inventoryRequest.getNumber(),
inventoryRequest.getTrackingEnabled(),
inventoryRequest.getTransfers(),
inventoryRequest.getAssignment(),
inventoryRequest.getNotes());
this.completeSetsQuantity = completeSetsQuantity;
this.specificProductQuantity = specificProductQuantity;
}
private Long completeSetsQuantity;
private Long specificProductQuantity;
}
And this is method i tried to do that.
#Transactional
public Page<? extends InventoryRequest> getInventoryRequestPage(InventoryRequestSearchParams searchParams, Pageable pageable) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<InventoryRequest> query = builder.createQuery(InventoryRequest.class);
Root<InventoryRequest> root = query.from(InventoryRequest.class);
Join<InventoryRequest, InventoryRequestAssignment> assignmentJoin = root.join(InventoryRequest.ASSIGNMENT, JoinType.LEFT);
Expression<Long> specificProductQuantity = builder.count(builder.selectCase()
.when(assignmentJoin.get(InventoryRequestAssignment.CATALOG).isNotNull(), 1)
.otherwise(0));
Expression<Long> completeSetsQuantity = builder.count(builder.selectCase()
.when(assignmentJoin.get(InventoryRequestAssignment.CATALOG).isNull(), 1)
.otherwise(0));
Predicate predicate = InventoryRequestSpecificationComposer.builder()
.searchParams(searchParams)
.build()
.compose()
.toPredicate(root, query, builder);
query.select(
builder.construct(InventoryRequestDTO.class,
root,
completeSetsQuantity,
specificProductQuantity))
.where(predicate);
Query q = entityManager.createQuery(query);
int totalRows = q.getResultList().size();
q.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
q.setMaxResults(pageable.getPageSize());
return new PageImpl<>(q.getResultList(), pageable, totalRows);
}
But i get this exception.
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias2,role=com.connectsx.core.model.entity.InventoryRequest.createdBy,tableName=users,tableAlias=user2_,origin=inventory_request inventoryr0_,columns={inventoryr0_.created_by ,className=com.connectsx.core.model.entity.User}}]
Also I've got specification builder for audit entity, which fetches organization, created_by, modified_by properties. But if i select InventoryRequest.class, it works fine, and with DTO it fails.

There is no point in using a constructor expression if you pass the whole entity. You could just as well fetch the entity and call the constructor on the result list objects. However, if you really want a performance improvement, this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A sample model for your use case could look like the following:
#EntityView(InventoryRequest.class)
public interface InventoryRequestDTO {
#IdMapping
UUID getId();
String getName();
String getEmail();
UserDTO getRequester();
UserDTO getSource();
InventoryRequestStatus getStatus();
String getCarrier();
String getTrackingNumber();
String getNote();
EventIdView getEvent();
Long getNumber();
Boolean getTrackingEnabled();
#Mapping(fetch = FetchStrategy.MULTISET)
Set<TransferDTO> getTransfers();
#Mapping(fetch = FetchStrategy.MULTISET)
Set<InventoryRequestAssignmentDTO> getAssignment();
#Mapping(fetch = FetchStrategy.MULTISET)
Set<InventoryRequestNoteDTO> getNotes();
#Mapping("COUNT(*) FILTER (WHERE assignment.catalog IS NULL)")
Long getCompleteSetsQuantity();
#Mapping("COUNT(*) FILTER (WHERE assignment.catalog IS NOT NULL)")
Long getSpecificProductQuantity();
}
#EntityView(User.class)
public interface UserDTO {
#IdMapping
UUID getId();
// Other fields you need
}
#EntityView(Transfer.class)
public interface TransferDTO {
#IdMapping
UUID getId();
// Other fields you need
}
#EntityView(InventoryRequestAssignment.class)
public interface InventoryRequestAssignmentDTO {
#IdMapping
UUID getId();
// Other fields you need
}
#EntityView(InventoryRequestNote.class)
public interface InventoryRequestNoteDTO {
#IdMapping
UUID getId();
// Other fields you need
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
InventoryRequestDTO dto = entityViewManager.find(entityManager, InventoryRequestDTO.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features i.e. have a repository similar to the following
#Repository
public interface InventoryRepository {
Page<InventoryRequestDTO> findAll(Specification specification, Pageable pageable);
}
It will only fetch what you tell it to. Enjoy!

Related

Hibernate query to filter results from a nested object list

As a followup to this answer(on approach 1 ) I want to go a step further :
I want to Filter the grand child objects based on certain criteria's. I tried the following query, but it still does not filter out the objects under the grandchild entity.
#Query("select ch from ChildEntity ch "
+ " join ch.parentEntity pr "
+ " join fetch ch.grandChildEntities gc "
+ " where pr.bumId = :bumId and ch.lastExecutionTimestamp in "
+ "( select max(ch1.lastExecutionTimestamp) from ChildEntity ch1 "
+ "join ch1.grandChildEntities gc ON ch1.id = gc.childEntity where "
+ "gc.field1 in ('\"Criteria1\"','\"Criteria2\"','\"Criteria3\"') and "
+ "gc.field2 = '\"soldout\"'"
+ "ch1.parentEntity = pr group by ch1.c1))")
List<ChildEntity> findLastExecutedChildFromBumId(#Param("bumId") String bumId);
Associated Class Entities
Class Relation ParentEntity <1-oneToMany-x> ChildEntity<1-oneToMany-x>GrandChildEntity
#Entity
#Getter
#Setter
#Table(name = "table_parent")
#RequiredArgsConstructor
#NoArgsConstructor
#AllArgsConstructor
public class ParentEntity implements Serializable {
private static final long serialVersionUID = -271246L;
#Id
#SequenceGenerator(
name="p_id",
sequenceName = "p_sequence",
initialValue = 1,
allocationSize = 1)
#GeneratedValue(generator="p_id")
#Column(name="id", updatable=false, nullable=false)
private Long id;
#NonNull
#Column(name ="bum_id", nullable = false, unique = true)
private String bumId;
#NonNull
#Column(nullable = false, length = 31)
private String f1;
#NonNull
#Column(nullable = false, length = 31)
private String f2;
#NonNull
#Column( nullable = false, length = 255)
#Convert(converter = JpaConverterJson.class)
private List<String> f3;
#NonNull
#Column(nullable = false)
private String f4;
#NonNull
#Column(name = "es_status", nullable = false, length = 255)
#Enumerated(EnumType.STRING)
private ExecutionStatus esStatus;
#JsonManagedReference
#OneToMany(mappedBy = "parentEntity", cascade = CascadeType.ALL,
fetch = FetchType.EAGER)
#Setter(AccessLevel.NONE)
private List<ChildEntity> childEntities;
public void setChildEntities(List<ChildEntity> childEntities) {
this.childEntities = childEntities;
childEntities.forEach(entity -> entity.setParentEntity(this));
}
}
#Entity
#Getter
#Setter
#Table(name= "table_child")
#NoArgsConstructor
public class ChildEntity implements Serializable {
private static final long serialVersionUID = -925587271547L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER )
#JoinColumn(name = "parent_id")
private ParentEntity parentEntity;
#Column(name = "c1",nullable = false)
#NonNull
#Convert(converter = JpaConverterJson.class)
private String c1;
#Column(name = "last_exec_status",nullable = false)
#NonNull
#Enumerated(EnumType.STRING)
private ExecutionStatus lastExecStatus;
#Column(name = "c4",nullable = false)
#NonNull
private String c4;
#Column(name = "last_execution_timestamp",nullable = false)
#NonNull
private long lastExecutionTimestamp;
#JsonManagedReference
#NonNull
#OneToMany(mappedBy = "childEntity", cascade = CascadeType.ALL,
fetch = FetchType.EAGER)
#Setter(AccessLevel.NONE)
private List<GrandChildEntity> grandChildEntities;
public void setGrandChildEntities(List<GrandChildEntity> grandChildEntities) {
this.grandChildEntities = grandChildEntities;
grandChildEntities.forEach(entity -> entity.setChildEntity(this));
}
}
#Entity
#Getter
#Setter
#Table(name="table_grand_child")
#NoArgsConstructor
//#AllArgsConstructor
public class GrandChildEntity implements Serializable {
private static final long serialVersionUID = -925567241248L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER )
#JoinColumn(name = "child_entity_id")
private ChildEntity childEntity;
#Column(name="gc1",nullable = false)
private String gc1;
#Column(name="gc2",nullable = false)
private String gc2;
#Column(name="gc3",nullable = false)
private String gc3;
#Column(name="gc3",nullable = true)
private List<String> gc3;
}
Filtering a collection that is join fetched is a bad idea as that alters the "persistent state" and might cause entities to be removed due to that. I suggest you use a DTO approach instead.
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(ChildEntity.class)
public interface ChildEntityDto {
#IdMapping
Long getId();
String getC1();
ParentEntityDto getParentEntity();
#Mapping("grandChildEntities[field1 in ('\"Criteria1\"','\"Criteria2\"','\"Criteria3\"') and gc.field2 = '\"soldout\"']")
Set<GrandChildEntityDto> getGrandChildEntities();
#EntityView(ParentEntity.class)
interface ParentEntityDto {
#IdMapping
Long getId();
String getF1();
}
#EntityView(GrandChildEntity.class)
interface GrandChildEntityDto {
#IdMapping
Long getId();
String getGc1();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Mapping a database view entity with a simple entity and pass to DTO using Spring Data

I'm just learning Spring Data. I want to map a database view Entity with a simple Entity and pass to DTO which will contain columns both entities. I understand that I can use a special database view but I need to map precisely entities of Spring Data.
I have a database view Entity "MentorStudents":
#Entity
#Table(name = "mentor_students")
#Immutable
public class MentorStudents implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "mentor_id", updatable = false, nullable = false)
private Long mentorId;
//This entity I need to map
private Mentor mentor;
#Column(name = "active_students")
private Integer activeStudents;
public MentorStudents() {
}
//getters, setters, equals, hashCode
}
A database view sql of an above entity is:
SELECT id AS mentor_id, active_students
FROM mentor
LEFT JOIN ( SELECT mentor_id, count(mentor_id) AS active_students
FROM contract
WHERE close_type IS NULL
GROUP BY mentor_id) active ON mentor.id = active.mentor_id
ORDER BY mentor.id;
And I have a simple Entity "Mentor":
#Entity
#Table(name = "mentor")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#NotNull
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "patronymic")
private String patronymic;
#Column(name = "phone")
private String phone;
#NotNull
#Column(name = "email", nullable = false)
private String email;
#Column(name = "skype")
private String skype;
#Column(name = "country")
private String country;
#Column(name = "city")
private String city;
#Column(name = "max_students")
private Long maxStudents;
//getters, setters, equals, hashCode
I have to get a DTO which contains all Mentor fields and an "activeStudents" MentorStudents field without a "mentorId" field. How do it?
Use spring data projection:
public interface YourDto {
// all Mentor get fields
String getFirstName();
...
// activeStudents get field
Integer getActiveStudents();
}
public interface YourRepository extends JpaRepository<YourEntity, Integer> {
#Query(value = "select ...(all fields match YourDto) from Mentor m, MentorStudents s where m.id = s.mentorId and m.id = ?1")
Optional<YourDto> findMyDto(Integer mentorId);
}

Tweek performance of JPA OneToMany

I have following JPA entity:
Entity
#Entity
#Table(name = "todo")
#NamedEntityGraph(name = "TodoEntity.children",
attributeNodes = #NamedAttributeNode("children"))
public class TodoEntity {
public TodoEntity() {
}
#Id
#GeneratedValue
private Long id;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long userId;
private String text;
private String description;
private Boolean collapsed;
private Boolean completed;
private Integer sortOrder;
private Date expiredDate;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
private Integer priority;
private String guid;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#JsonIgnore
#Column(updatable = false,nullable = false)
private Long parentId;
#OneToMany(fetch = FetchType.EAGER)
#Cascade({CascadeType.ALL})
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "parentId", referencedColumnName = "id")
#OrderBy("sortOrder ASC")
private List<TodoEntity> children;
}
Repo
#EntityGraph(value = "TodoEntity.children", type = EntityGraph.EntityGraphType.FETCH)
Page<TodoEntity> findByUserIdAndParentId(Long userId, Long parentId, Pageable pageable);
I want to have a TodoList that I can infinitely nest.
The code is working so far - only thing is that when I have around 1500 todos and they are nested, SQL and the queries are becoming slow (around 1,3s)
How can I improve this?
IDEA:
Actually I think it would be enough to just get all Todos in the database with one query (that takes 2,2 ms) and then nesting them with java (having the parentId) so that the workload is on the application layer.

Entity for joining multiple tables

I am using hibernate criteria query framework for generating reports. I have to provide sorting and filtering as well. Things were working fine when the data was confined to a single entity. However I have a requirement to join multiple entities and show the result in a single table. Following are the entities:
#Entity
#Table(name = "user_profile")
#Where(clause = "deleted = 0")
public class UserProfile {
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Column(name = "username")
private String username;
#Column(name = "email")
private String email;
#Column(name = "first_name")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "last_name")
private String lastName;
}
#Entity
#Table(name = "user_data")
public class UserData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "account_nonexpired")
private Boolean accountNonExpired = true;
#Column(name = "account_nonlocked")
private Boolean accountNonLocked = true;
#Column(name = "credentials_nonexpired")
private Boolean credentialsNonExpired = true;
#Column(name = "enabled")
private Boolean enabled = false;
}
#Entity
#Table(name = "user_role")
public class Role {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "username")
private String username;
#Column(name = "role")
private String role;
}
These entities have a common user name. Is it possible to create an entity which has no table but just contains these entities as fields? For eg:
public Class UserDataProfileRoleMapping{
private UserProfile userProfile;
private List<Role> role;
private UserData userData;
}
I can create a mapping table but I was keeping it as a last resort.
Edit
The query which I want to fire is something like:
select * from user_data u, user_role r, user_profile up
where
u.username = r.username and
r.username = up.username;
You should create a POJO as a DTO which will hold exactly the information you need and use that instead of the actual entities. Let's assume we have three entities, Order, OrderItem and Customer and the query should be something like
SELECT Order.orderDate, Customer.name, OrderItem.amount
FROM Order
JOIN Customer ON Order.customerId = Customer.id
JOIN OrderItem ON Order.id = OrderItem.orderId
WHERE OrderItem.name = 'Puppet';
Now, the DTO would be:
public class ReturnDto {
private Date date;
private String customerName;
private int amount;
public ReturnDto(Date date, String customerName, int amount) {
this.date = date;
...
}
// getters for the three properties
}
And in your DAO you could do something along the following lines:
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<ReturnDto> cQuery = cb.createQuery(ReturnDto.class);
Root<Order> orderRoot = cQuery.from(Order.class);
Join<Order, Customer> customerJoin = orderRoot.join(Order_.customer);
Join<Order, OrderItem> orderItemJoin = orderRoot.join(Order_.orderItems);
List<Predicate> criteria = new ArrayList<Predicate>();
criteria.add(cb.equal(orderItemJoin.get(OrderItem_.name), "Puppet");
// here you can do the sorting, e.g. - all with the criteria API!
cQuery.orderBy(...);
cQuery.distinct(true);
cQuery.select(cb.construct(ReturnDto.class,
orderRoot.get(Order_.date),
customerJoin.get(Customer_.name),
orderItemJoin.get(OrderItem_.amount)
));
cQuery.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
List<ReturnDto> returnList = entityManager.createQuery(cQuery).getResultList();
Obviously, you cannot save the items in the returned list, but you get exactly the information you want and you can still handle things with the list, which you cannot handle with SQL/Criteria API.
Update: Just found this link, which may also help with the concept I used above: http://www.javacodegeeks.com/2013/04/jpa-2-0-criteria-query-with-hibernate.html?utm_content=buffer0bd84&utm_source=buffer&utm_medium=twitter&utm_campaign=Buffer

Hibernate: Multiple filters on an entity

I want to have multiple Hibernate filters on an entity, I have tried everything logical without luck and Google has come up short on this one, as has the Hibernate doc. I can't imagine that this is not possible. (Using Java 6 Hibernate 4.1.9.final)
Currently, I have this:
#Entity
#Table(name = "CATEGORY")
public class Category implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "CATEGORYID")
private int ID;
#Column(name = "CATEGORYNAME")
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "CATEGORYID")
#OrderBy("TESTCASEID desc")
#Filter(name = "TEST_RUN_ID_FILTER")
private Collection<TestCase> testCases;
...
}
#Entity
#Table(name = "TESTCASE_NEW")
#FilterDef(name = "TEST_RUN_ID_FILTER", defaultCondition = "TESTRUNID in (:IDS)", parameters = { #ParamDef(name = "IDS", type = "int") })
public class TestCase implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "TESTCASEID")
private int ID;
#Column(name = "TESTCASENAME")
private String name;
...
}
I want to add a second independent filter to the Testcase class. What I am after is something like this:
Select ...
From CATEGORY INNER JOIN TESTCASE on CATEGORY.CATEGORYID = TESTCASE.CATEGORYID
Where TESTCASE.TESTRUNID in (....)
and TESTCASE.TESTCASENAME like '%..%'
This is what I tried
I tried adding multiple #FilterDefs to TestCase like such, but that didn't compile:
#Entity
#Table(name = "TESTCASE_NEW")
#FilterDef(name = "TEST_RUN_ID_FILTER", defaultCondition = "TESTRUNID in (:IDS)",
parameters = { #ParamDef(name = "IDS", type = "int") })
#FilterDef(name = "TESTCASE_NAME_FILTER", defaultCondition = "TESTCASENAME like :TESTCASE_NAME",
parameters = { #ParamDef(name = "TESTCASE_NAME", type = "string") })
public class TestCase implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "TESTCASEID")
private int ID;
#Column(name = "TESTCASENAME")
private String name;
...
}
The Hibernate documentation led to to try something like this which complained the testrunid filter was non-existent
#Entity
#Table(name = "CATEGORY")
public class Category implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "CATEGORYID")
private int ID;
#Column(name = "CATEGORYNAME")
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "CATEGORYID")
#OrderBy("TESTCASEID desc")
private Collection<TestCase> testCases;
...
}
#Entity
#Table(name = "TESTCASE_NEW")
#FilterDef(name = "TESTCASE_FILTER", parameters = { #ParamDef(name = "IDS", type = "int"), #ParamDef(name = "TESTCASE_NAME", type = "string") })
#Filters({ #Filter(name = "TEST_RUN_ID_FILTER", condition = "TESTRUNID in (:IDS)"), #Filter(name = "TESTCASE_NAME_FILTER", condition = "TESTCASENAME like :TESTCASE_NAME") })
// #FilterDef(name = "TEST_RUN_ID_FILTER", defaultCondition = "TESTRUNID in (:IDS)", parameters = { #ParamDef(name =
// "IDS", type = "int") })
public class TestCase implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "TESTCASEID")
private int ID;
#Column(name = "TESTCASENAME")
private String name;
...
}
#SuppressWarnings("unchecked")
public List<Category> getCategories(List<Integer> testRunIDs, String category, String testCaseName)
{
Session session = getSession();
session.enableFilter("FILE_TYPE_FILTER");
if (testRunIDs != null && testRunIDs.size() != 0)
{
session.enableFilter("TEST_RUN_ID_FILTER").setParameterList("IDS", testRunIDs);
}
if (category != null && !category.equals("0") && !category.equals(""))
{
session.enableFilter("CATEGORY_FILTER").setParameter("CATEGORY", category);
}
/*
* Hibernate wants to do an (left) outer join be default.
* This bit of HQL is required to get it to do an inner join.
* The query tells Hibernate to do an inner join on the testCases property inside the Category object
*/
Query query = session.createQuery("select distinct c from Category c inner join c.testCases tc");
List<Category> result = query.list();
return result;
..
}
Your help is greatly appreciated
I've solved it actually, but thanks for the help. The solution (detailed below) is to wrap multiple #FilterDef annotations in a #FilterDefs annotation. Oddly enough I didn't find this anywhere or in the Hibernate doc, I saw this post (Multiple annotations of the same type on one element?), and thought hey maybee #FilterDefs exists and it does.
#Entity
#Table(name = "TESTCASE_NEW")
#FilterDefs({
#FilterDef(name = "TESTCASE_NAME_FILTER", defaultCondition = "TESTCASENAME like :TESTCASENAME", parameters = { #ParamDef(name = "TESTCASENAME", type = "string") }),
#FilterDef(name = "TEST_RUN_ID_FILTER", defaultCondition = "TESTRUNID in (:IDS)", parameters = { #ParamDef(name = "IDS", type = "int") })
})
public class TestCase implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "TESTCASEID")
private int ID;
#Column(name = "TESTCASENAME")
private String name;
...
}
#Entity
public class Category implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "CATEGORYID")
private int ID;
#Column(name = "CATEGORYNAME")
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "CATEGORYID")
#OrderBy("TESTCASEID desc")
#Filters({
#Filter(name = "TEST_RUN_ID_FILTER"),
#Filter(name = "TESTCASE_NAME_FILTER") })
private Collection<TestCase> testCases;
...
}
In the DAO, I just turn on the ones I need
public List<Category> getCategories(List<Integer> testRunIDs, String category, String testCaseName)
{
Session session = getSession();
if (testRunIDs != null && testRunIDs.size() != 0)
{
session.enableFilter("TEST_RUN_ID_FILTER").setParameterList("IDS", testRunIDs);
}
if (testCaseName != null)
{
session.enableFilter("TESTCASE_NAME_FILTER").setParameter("TESTCASENAME", testCaseName);
}
/*
* Hibernate wants to do an (left) outer join be default.
* This bit of HQL is required to get it to do an inner join.
* The query tells Hibernate to do an inner join on the testCases property inside the Category object
*/
Query query = session.createQuery("select distinct c from Category c inner join c.testCases tc");
List<Category> result = query.list();
return result;
}

Categories

Resources