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;
}
Related
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!
I've designed some tables for my application where I've placed something like that:
product productversion
--- ---
productId productVersionId
productVersionId (ref) productId (ref)
name length
height
where we have relation one to many. I just need some base product with parameters which can change and versioned parameters so I can watch it's history. Now... I have problem with Persisting those objects. I've tried multiple ways, this is oldest one:
Product p = new Product();
p.name = "some name";
Productversion pv = new Productversion();
pv.length = 10;
pv.height = 20;
p.productversionId = pv;
But while trying to persist this object I've getting error that productId cannot be null and it refers to field productId inside Productversion object. How can I do this?
[update]
Some more information about my problem:
public class Product implements Serializable {
#JoinColumn(name = "productversionId", referencedColumnName = "productversionId")
#ManyToOne(optional = true, cascade = CascadeType.PERSIST)
private Productversion productversionId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "productId")
private Collection<Productversion> productversionCollection;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ProductId")
private Integer productId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "Name")
private String name = null;
// well I believe construct and getter/setter not important now
And second Entity:
public class Productversion implements Serializable {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "productversionId")
private Collection<Productiondata> productiondataCollection;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "productversionId")
private Integer productversionId;
#Basic(optional = false)
#NotNull
#Column(name = "parameter")
private float weight;
And the way I create new object (just have no idea how can I do this):
public Product getNewProduct() {
if (newProduct == null) {
this.newProduct = new Product();
Productversion pv = new Productversion();
pv.setProductId(newProduct);
newProduct.setProductversionId(pv);
}
return newProduct;
}
And the way I'm trying to persist object:
public Boolean addEntry(Product productData) {
Boolean status = false;
DbConnector dc = new DbConnector();
EntityManager em = dc.getEntityManager();
try {
EntityTransaction et = em.getTransaction();
et.begin();
em.persist(productData);
et.commit();
status = true;
} finally {
em.close();
}
return status;
}
Seems like you missing on the set for the id field in your Product object.
Product p = new Product();
p.id = 1; <<<<<<-------------------
p.name = "some name";
I am trying to get named entity graphs to work properly. Basically, I have a Customer who can have zero or more Addresses. When I query to get a list of Customers, I want the customer's fields, but not the associated Addresses. When I query for a particular Customer, I want its fields and all of the associated Addresses. So, I've been trying to use named entity graphs because they seem to address this situation. I created one to get basic information, and another that gets everything. Unfortunately, when I use either graph, I still always get everything. I am sure that I am making a simple mistake, but I cannot seem to find it. I'd appreciate your help to figure out my error. The relevant code follows...
Thank you for your time and advise,
Mike
/////////////////////////////////////////////////////////
// ENTITIES
/////////////////////////////////////////////////////////
#Entity
#Table(name = "customers")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c")})
#NamedEntityGraphs({
#NamedEntityGraph(
name="previewCustomerEntityGraph",
attributeNodes={
#NamedAttributeNode("id"),
#NamedAttributeNode("displayAs"),
#NamedAttributeNode("rowVersion")
}
),
#NamedEntityGraph(
name="fullCustomerEntityGraph",
attributeNodes={
#NamedAttributeNode("id"),
#NamedAttributeNode("displayAs"),
#NamedAttributeNode("rowVersion"),
#NamedAttributeNode(value = "addressCollection", subgraph = "addressCollection")
},
subgraphs = #NamedSubgraph(
name = "addressCollection",
attributeNodes = {
#NamedAttributeNode("id"),
#NamedAttributeNode("displayAs"),
#NamedAttributeNode("rowVersion")
}
)
)
})
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "Id")
#XmlAttribute(required=true)
private Long id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 256)
#Column(name = "DisplayAs")
#XmlElement(required=true, nillable=false)
private String displayAs;
#Basic(optional = true)
#Size(max = 256)
#Column(name = "Name")
#XmlElement(required=true, nillable=true)
private String name;
#Basic(optional = false)
#NotNull
#XmlElement(required=true, nillable=false)
#Column(name = "RowVersion")
#Version
private Timestamp rowVersion;
#OneToMany(mappedBy = "customerId",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<Address> addressCollection;
//#XmlTransient
public Collection<Address> getAddressCollection() {
return addressCollection;
}
public void setAddressCollection(Collection<Address> addressCollection) {
this.addressCollection = addressCollection;
}
// Object methods have been removed to save space
}
#Entity
#Table(name = "addresses")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Address.findAll", query = "SELECT a FROM Address a")})
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#XmlAttribute(required=true)
#Column(name = "Id")
private Long id;
#Basic(optional = false)
#NotNull
#Column(name = "RowVersion")
#Temporal(TemporalType.TIMESTAMP)
#XmlElement(required=true, nillable=true)
private Date rowVersion;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 256)
#Column(name = "DisplayAs")
#XmlElement(required=true, nillable=true)
private String displayAs;
#JoinColumn(name = "CustomerId", referencedColumnName = "Id")
#ManyToOne
private Customer customerId;
// Object methods have been removed to save space
}
/////////////////////////////////////////////////////////
// QUERIES
/////////////////////////////////////////////////////////
protected T find(Object id) {
// Filter to only return entities belonging to the tenant.
EntityGraph eg = getEntityManager().getEntityGraph("fullCustomerEntityGraph");
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
javax.persistence.criteria.Root<T> from = cq.from(entityClass);
javax.persistence.criteria.CriteriaQuery<T> select = cq.select(from);
cq.where(cb.and(
cb.equal(from.<T>get("id"), id)
));
javax.persistence.TypedQuery<T> tq = getEntityManager().createQuery(select);
tq.setHint("javax.persistence.fetchgraph", eg);
return getSingleResultOrNull(tq);
}
protected List<T> findAll() {
// Filter to only return entities belonging to the tenant.
EntityGraph eg = getEntityManager().getEntityGraph("previewCustomerEntityGraph");
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
javax.persistence.criteria.Root<T> from = cq.from(entityClass);
javax.persistence.criteria.CriteriaQuery<T> select = cq.select(from);
javax.persistence.TypedQuery<T> tq = getEntityManager().createQuery(select);
tq.setHint("javax.persistence.fetchgraph", eg);
return tq.getResultList();
}
protected T getSingleResultOrNull(javax.persistence.TypedQuery<T> query) {
query.setMaxResults(1);
List<T> list = query.getResultList();
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
The serializer is calling getAddressCollection.
Note that #XmlTransient is commented on address list. Place this annotation back and try again.
In this case auto serialization will allways avoid the list. If you need to return the full graph somewhere else I recommend writing your own serialization logic or create a Value Object to hold exactly what you need and let serializer do the job.
The application works well with the generated methods and classes, but I wanted to get a list of article that matching a specific idChapitre. I get a "HTTP Status 404 - Not Found" on my browser.
This is the facade class, where I have put in the findByIdChapitre method:
#Stateless
#Path("articles")
public class ArticleFacadeREST extends AbstractFacade<Article> {
#PersistenceContext(unitName = "PNC-BackendPU")
private EntityManager em;
public ArticleFacadeREST() {
super(Article.class);
}
#GET
#Path("bychapitre/{idChapitre}")
#Produces({"application/json"})
public List<Article> findByIdChapitre(#PathParam("idChapitre") Chapitre idChapitre) {
Query q = em.createNamedQuery("Article.findByIdChapitre");
q.setParameter("idChapitre", idChapitre);
return q.getResultList();
}
...
}
and my article class:
#Entity
#Table(name = "article")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Article.findAll", query = "SELECT a FROM Article a"),
#NamedQuery(name = "Article.findByIdArticle", query = "SELECT a FROM Article a WHERE a.idArticle = :idArticle"),
#NamedQuery(name = "Article.findByIdChapitre", query = "SELECT a FROM Article a WHERE a.idChapitre = :idChapitre"),
#NamedQuery(name = "Article.findByCodeArticle", query = "SELECT a FROM Article a WHERE a.codeArticle = :codeArticle")})
public class Article implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_article", nullable = false)
private Integer idArticle;
#Lob
#Size(max = 65535)
#Column(name = "nom_article", length = 65535)
private String nomArticle;
#Size(max = 20)
#Column(name = "code_article", length = 20)
private String codeArticle;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idArticle")
private List<Littera> litteraList;
#JoinColumn(name = "id_chapitre", referencedColumnName = "id_chapitre", nullable = false)
#ManyToOne(optional = false)
private Chapitre idChapitre;
public Article() {
}
public Article(Integer idArticle) {
this.idArticle = idArticle;
}
Getters and setters
}
and then my chapitreclass:
....
#Entity
#Table(name = "chapitre")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Chapitre.findAll", query = "SELECT c FROM Chapitre c"),
#NamedQuery(name = "Chapitre.findByIdChapitre", query = "SELECT c FROM Chapitre c WHERE c.idChapitre = :idChapitre"),
#NamedQuery(name = "Chapitre.findByIdSection", query = "SELECT c FROM Chapitre c WHERE c.idSection = :idSection"),
#NamedQuery(name = "Chapitre.findByCodeChapitre", query = "SELECT c FROM Chapitre c WHERE c.codeChapitre = :codeChapitre")})
public class Chapitre implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_chapitre", nullable = false)
private Integer idChapitre;
#Lob
#Size(max = 65535)
#Column(name = "nom_chapitre", length = 65535)
private String nomChapitre;
#Size(max = 20)
#Column(name = "code_chapitre", length = 20)
private String codeChapitre;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idChapitre")
private List<Dossier> dossierList;
#JoinColumn(name = "id_section", referencedColumnName = "id_section", nullable = false)
#ManyToOne(optional = false)
private Section idSection;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idChapitre")
private List<Article> articleList;
public Chapitre() {
}
public Chapitre(Integer idChapitre) {
this.idChapitre = idChapitre;
}
Getters and setters
}
I'm trying to simply filter out results based on the ID property of a child class in a OneToMany relationship, but Hibernate (4.1.9.Final) is generating a Left Outer Join instead of an inner join and thus returning results I don't want.
Model:
Parent:
#Entity
#Table(name = "CATEGORY")
public class Category
{
#Id
#Column(name = "CATEGORYID")
private int ID;
#Column(name = "CATEGORYNAME")
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "CATEGORYID")
#Filter(name = "TEST_RUN_ID_FILTER")
private Collection<TestCase> testCases
...
}
Child:
#Entity
#Table(name = "TESTCASE_NEW")
#FilterDef(name = "TEST_RUN_ID_FILTER", defaultCondition = "TESTRUNID in (:IDS)", parameters = { #ParamDef(name = "IDS", type = "int") })
public class TestCase
{
#Id
#Column(name = "TESTCASEID")
private int ID;
#Column(name = "TESTCASENAME")
private String name;
#Column(name = "STATUS")
private String status;
#Column(name = "TESTRUNID")
private int testRunId;
...
}
DAO:
public List<Category> getAllCategoriesForTestRuns(List<Integer> testRunIDs)
{
Session session = getSession();
session.enableFilter("TEST_RUN_ID_FILTER")
.setParameterList("IDS", testRunIDs);
Query query = session.createQuery("FROM " + Category.class.getSimpleName());
List<Category> result = query.list();
return resul2t;
}
How can I tell Hibernate to use an inner join ?
You can try something as follows:
select c from Category c inner join c.testCases tc