java springframework.data.jpa generates same join twice using JpaSpecificationExecutor - java

i'am using JpaSpecificationExecutor so i create my sql dynamically ,using specification Api from org.springframework.data.jpa.domain.Specification and the probleme is that it generates the same join twice ,which tears down performances ,here is the code :
the caller method is :
private Specification<EntityA> toSpecEntityBAndEntityC(boolean withEntityBAndEntityC, Specification<EntityA> specs) {
if (withEntityBAndEntityC) {
specs = Specification.where(specs).and(EntityASpecs.withEntityB());
specs = Specification.where(specs).and(EntityASpecs.withEntityC());
}
return specs;
}
here is the EntityASpecs class
public interface EntityASpecs{
static Specification<EntityA> withEntityB() {
return (root, query, builder) -> {
if (!isCountQuery(query)) {
root.fetch(EntityA_.EntityB);
} else {
root.join(EntityA_.EntityB,JoinType.INNER);
}
return null;
};
}
static Specification<EntityA> withEntityC() {
return (root, query, builder) -> {
if (!isCountQuery(query)) {
root.fetch(EntityA_.EntityC);
} else {
root.join(EntityA_.EntityC,JoinType.INNER);
}
return null;
};
}
}
here are the entities:
#Data
#Entity
public class EntityA{
#Id
#GeneratedValue(generator = "seq_entityB")
#SequenceGenerator(name = "seq_entityB", sequenceName = "SEQ_entityB", allocationSize = 1)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
private EntityB entityB;
#ManyToOne(fetch = FetchType.LAZY)
private EntityC entityC ;
}
it generates this query
select *
from (select entityA0_.id as id1_4_0_,
entityC2_.filedOne as filedOne1_3_1_,
entityB3_.filedTwo as filedTwo1_1_2_,
entityA0_.filedFive_id as filedFive_id7_4_0_,
entityA0_.entityB_filedTwo as entityB_filedTwo8_4_0_,
entityA0_.DATE_TRAITEMENT as DATE_TRAITEMENT2_4_0_,
entityA0_.filedFour as filedFour3_4_0_,
entityA0_.entityC_filedOne as entityC_filedOne9_4_0_,
entityA0_.filedSix as filedSix4_4_0_,
entityA0_.filedSeven as filedSeven5_4_0_,
entityA0_.TEMPS_REPONSE as TEMPS_REPONSE6_4_0_,
entityC2_.NB_MOYEN_TRAITEMENTS as NB_MOYEN_TRAITEMEN2_3_1_,
entityC2_.filedSeven as filedSeven3_3_1_,
entityC2_.filedSixteen as filedSixteen4_3_1_,
entityB3_.filedFifteen as DATE_TRAITEMENT_SA2_1_2_,
entityB3_.filedEight as filedEight11_1_2_,
entityB3_.filedTen as filedTen3_1_2_,
entityB3_.filedNine as filedNine4_1_2_,
entityB3_.filedFourteen as filedFourteen5_1_2_,
entityB3_.filedThirteen as filedThirteen6_1_2_,
entityB3_.NUMERO_AUTORISATION as NUMERO_AUTORISATIO7_1_2_,
entityB3_.filedEleven as filedEleven8_1_2_,
entityB3_.filedSeven as filedSeven9_1_2_,
entityB3_.filedTwelve as filedTwelve10_1_2_
from entityA entityA0_
inner join entityB entityB1_ on entityA0_.entityB_filedTwo = entityB1_.filedTwo
inner join entityC entityC2_ on entityA0_.entityC_filedOne = entityC2_.filedOne
**inner join entityB entityB3_ on entityA0_.entityB_filedTwo = entityB3_.filedTwo**
where entityA0_.filedSix = 'Value'
and (entityB1_.filedFifteen between TO_DATE('2018-11-08 14:00:00', 'YYYY-MM-DD HH24:MI:SS') and TO_DATE('2018-11-08 15:00:00', 'YYYY-MM-DD HH24:MI:SS'))
order by entityA0_.DATE_TRAITEMENT desc)
;
NB:
duplicate join is between two stars
**inner join entityB entityB3_ on entityA0_.entityB_filedTwo = entityB3_.filedTwo**

Related

Spring JPA Specification filtering

I'm trying to filter parent entity by intersection of children property and inputed list.
My entities:
#Entity
#Table(name = "recipes")
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Ingredient> ingredientsList = new ArrayList<>();
//other fields and methods
}
#Entity
#Table(name = "ingredients")
public class Ingredient {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "product")
private String product;
//other fields and methods
}
As result i want get list of Recipes. Each recipe must have all products from user's inputed list.
I was trying smth like this:
public static Specification<Recipe> containsProducts(List<String> products) {
return (root, query, criteriaBuilder) -> {
if (Objects.isNull(products) || products.isEmpty()) {
return criteriaBuilder.conjunction();
}
List<Predicate> predicates = new ArrayList<>();
Join<Recipe, Ingredient> ingredientRecipeJoin = root.join("ingredientsList", JoinType.INNER);
for (String product : products) {
if (product.isBlank()) {
continue;
}
predicates.add(criteriaBuilder.equal(ingredientRecipeJoin.get(Ingredient_.PRODUCT), product));
}
return criteriaBuilder.and(predicates.toArray(Predicate[]::new));
};
}
But it works only when products list size=1.
Finally i got answer. My miss is that i was trying to search recipes by only one join. For every item in user's inputed list i should create a new join and create on it a new prediction. Jpa logic prevent reusing columns from old joins.
So my containsProducts method now looks like:
public static Specification<Recipe> containsProducts(List<String> products) {
return (root, query, criteriaBuilder) -> {
if (Objects.isNull(products) || products.isEmpty()) {
return criteriaBuilder.conjunction();
}
List<Predicate> predicates = new ArrayList<>();
Join<Recipe, Ingredient> ingredientRecipeJoin;
for (String product : products) {
if (product.isBlank()) {
continue;
}
ingredientRecipeJoin = root.join(Recipe_.INGREDIENTS_LIST, JoinType.INNER);
predicates.add(criteriaBuilder.equal(ingredientRecipeJoin.get(Ingredient_.PRODUCT), product));
}
return criteriaBuilder.and(predicates.toArray(Predicate[]::new));
};
}

Query on distant/not directly related entity

I'm trying to create a query with a where clause on a not directly related table.
My table/entity structure looks like this:
I have got an entity Entity with a ManyToOne relation to Relation. Relation has a ManyToMany relation to DistantRelation.
I have a JpaSpecificationExecutor<Entity> on which I call findAll() with a Specification<Entity>.
How do I setup my entity and/or my specification so I can filter on one of the fields of DistantRelation?
Entities definition:
#javax.persistence.Entity
#Data
public class Entity {
#Id
private Long id;
#ManyToOne
private Relation relation;
}
#javax.persistence.Entity
public class Relation {
#Id
private Long id;
#ManyToMany
private Set<DistantRelation> distantRelation;
}
#Entity
public class DistantRelation {
#Id
private Long id;
private String name;
#ManyToMany
private Set<Relation> relation;
}
Solution 1. Subquery with optimal joins
public class EntityDistantRelationSpecification implements Specification<Entity> {
private String value;
public EntityDistantRelationSpecification(String value) {
this.value = value;
}
#Override
public Predicate toPredicate(Root<Entity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Subquery<DistantRelation> subQuery = query.subquery(DistantRelation.class);
Root<DistantRelation> subRoot = subQuery.from(DistantRelation.class);
Expression<Collection<Relation>> relations = subRoot.get("relation");
Predicate relationPredicate = builder.isMember(root.get("relation"), relations);
Predicate distantFiledPredicate = builder.equal(subRoot.get("name"), value);
subQuery.select(subRoot).where(relationPredicate, distantFiledPredicate);
return builder.exists(subQuery);
}
}
Generated query:
select
entity0_.id as id1_9_,
entity0_.relation_id as relation2_9_
from
entity entity0_
where
exists (
select
distantrel1_.id
from
distant_relation distantrel1_
where
(
entity0_.relation_id in (
select
relation2_.relation_id
from
distant_relation_relation relation2_
where
distantrel1_.id=relation2_.distant_relation_id
)
)
and distantrel1_.name=?
)
Solution 2. Subquery with all relation joins
public class EntityDistantRelationSpecification implements Specification<Entity> {
private String value;
public EntityDistantRelationSpecification(String value) {
this.value = value;
}
#Override
public Predicate toPredicate(Root<Entity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Subquery<Relation> subQuery = query.subquery(Relation.class);
Root<Relation> subRoot = subQuery.from(Relation.class);
Join<Relation, DistantRelation> distantRelationJoin = subRoot.join("distantRelation", JoinType.INNER);
Predicate relationPredicate = builder.equal(root.get("relation"), subRoot.get("id"));
Predicate distantFiledPredicate = builder.equal(distantRelationJoin.get("name"), value);
subQuery.select(subRoot).where(relationPredicate, distantFiledPredicate);
return builder.exists(subQuery);
}
}
Generated query:
select
entity0_.id as id1_9_,
entity0_.relation_id as relation2_9_
from
entity entity0_
where
exists (
select
relation1_.id
from
relation relation1_
inner join
relation_distant_relation distantrel2_
on relation1_.id=distantrel2_.relation_id
inner join
distant_relation distantrel3_
on distantrel2_.distant_relation_id=distantrel3_.id
where
entity0_.relation_id=relation1_.id
and distantrel3_.name=?
)

JPA: left join without #OneToMany annotations

I have a OneToMany relationship in my DB but I don't want that Hibernate manages it directly.
This relationships are translations, but a DTO represents itself a translated registry:
#Entity
#Table(name = "my_table")
public class MyTable {
#Id
#Column(name = "id", nullable = false, unique = true)
private Integer id;
#Transient
private String lang;
#Transient
private String text;
// getters and setters
...
}
#Entity
#Table(name = "my_table_translation")
public class MyTableTranslation {
#Id
#Column(name = "id", nullable = false, unique = false)
private Integer id;
#Id
#Column(name = "lang", nullable = false, unique = false, length = 2)
private String lang;
#Column(name = "text", nullable = false, unique = false, length = 200)
private String text;
// getters and setters
...
}
I want to have an specific findAll(String lang) method with a lang parameter, and use an Specification Criteria to build the query. Something like that:
public void findAll(String language) {
List<MyTable> list = repository.findAll(new Specification<MyTable>() {
#Override
public Predicate toPredicate(Root<MyTable> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// something there
return ...;
}
});
}
The fact is that I don't know how to do that, because I can't use JOIN clause, as I have not an attribute in the model that represents the relationship.
I tried to use the SELECT...FROM...LEFT JOIN query with SQL notation,
SELECT t1, t2 FROM MyTable t1 LEFT JOIN MyTableTranslation t2 ON t1.id = t2.id
and it works, but not as desired. The resulting list of objects, is a list of 2 object per item: one is the MyTable object, and the other is the MyTableTranslation related object. I need to parse the list and programatically build the objects using PropertyUtils class from Apache Commons library.
It is not clean I think... Does anybody know how to make it easy, without using SQL notation?
Marc, you can do the following to make it work and you do not need complicated join clauses or predicate right now. A simple implementation in embedded H2 database and JUnit testing will be sufficient for proof of concept (POC) as below
NOTE:
I am using Spring + Plain JPA with Hibernate implementation for POC.
I am using the Spring recommended way of managing transaction.
com.mycompany.h2.jpa package contains the entity classes.
Take a look at the mytable.sql which has similar structure to your needs.
MyTable.java
#Entity
#Table(name = "my_table")
public class MyTable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id", nullable = false, unique = true)
#JoinColumn(referencedColumnName="id", insertable=true, updatable=false)
private Long id;
#Column(name = "lang", unique=true)
private String lang;
#Column(name = "text")
private String text;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="id", insertable=true, updatable=true, referencedColumnName="id")
private List<MyTableTranslation> translations = new ArrayList<MyTableTranslation>();
...
// getters and setters, toString()
}
MyTableTranslation.java
#Entity
#Table(name = "my_table_translation")
public class MyTableTranslation implements Serializable {
private static final long serialVersionUID = 11L;
#Id
#Column(name = "id")
private Long id;
#Id
#Column(name = "speaker")
String speaker;
...
// getters and setters, toString()
}
TestH2DatabaseConfiguration.java
#Configuration
#EnableTransactionManagement
public class TestH2DatabaseConfiguration {
final static Logger log = LoggerFactory.getLogger(TestH2DatabaseConfiguration.class);
#Bean
#Qualifier("dataSource")
public DataSource h2DataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:mytable.sql").build();
}
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(h2DataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan("com.mycompany.h2.jpa");
factoryBean.setPersistenceUnitName("my_table");
Properties prop = new Properties();
prop.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
prop.put("hibernate.show_sql", "true");
prop.put("hibernate.hbm2ddl.auto", "none");
factoryBean.setJpaProperties(prop);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
#Bean
public MyTableDAO myTableDAO() {
return new MyTableDAOJPAImpl();
}
#Bean
public MyTableServiceImpl myTableService() {
MyTableServiceImpl myTableService = new MyTableServiceImpl();
myTableService.setMyTableDAO(myTableDAO());
return myTableService;
}
}
MyTableService.java
public interface MyTableService {
public MyTable saveMyTableTranslation(MyTable myTable);
public List<MyTable> getAllMyTables();
public MyTable getMyTable(Long entityId);
public MyTable getMyTable(String lang);
}
MyTableServiceImpl.java
#Transactional
public class MyTableServiceImpl implements MyTableService {
final static Logger log = LoggerFactory.getLogger(MyTableServiceImpl.class);
private MyTableDAO myTableDAO;
public void setMyTableDAO(MyTableDAO myTableDAO) {
this.myTableDAO = myTableDAO;
}
public MyTable saveMyTableTranslation(MyTable myTable) {
return myTableDAO.saveMyTableTranslation(myTable);
}
public List<MyTable> getAllMyTables() {
return myTableDAO.getAllMyTables();
}
public MyTable getMyTable(Long entityId) {
return myTableDAO.getMyTable(entityId);
}
public MyTable getMyTable(String lang) {
return myTableDAO.getMyTable(lang);
}
}
MyTableDAO.java
public interface MyTableDAO {
public MyTable saveMyTableTranslation(MyTable myTable);
public List<MyTable> getAllMyTables();
public MyTable getMyTable(Long entityId);
public MyTable getMyTable(String lang);
}
MyTableDAOJPAImpl.java
public class MyTableDAOJPAImpl implements MyTableDAO {
final static Logger log = LoggerFactory.getLogger(MyTableDAOJPAImpl.class);
#PersistenceContext
private EntityManager entityManager;
public MyTable saveMyTableTranslation(MyTable myTable) {
entityManager.persist(myTable);
return myTable;
}
#SuppressWarnings("unchecked")
public List<MyTable> getAllMyTables() {
return (List<MyTable>) entityManager.createQuery("FROM MyTable").getResultList();
}
public MyTable getMyTable(Long entityId) {
return (MyTable) entityManager.createQuery("FROM MyTable m WHERE m.id = :id ").setParameter("id", entityId).getSingleResult();
}
public MyTable getMyTable(String lang) {
return (MyTable) entityManager.createQuery("FROM MyTable m WHERE m.lang = :lang ").setParameter("lang", lang).getSingleResult();
}
}
MyTableTest.java (a JUnit test class)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestH2DatabaseConfiguration.class }, loader = AnnotationConfigContextLoader.class)
public class MyTableTest extends AbstractTransactionalJUnit4SpringContextTests {
final static Logger log = LoggerFactory.getLogger(MyTableTest.class);
#Autowired
#Qualifier("myTableService")
MyTableService myTableService;
#Test
public void test() throws ParseException {
MyTable parent = new MyTable();
parent.setLang("Italian");
parent.setText("Fast...");
MyTableTranslation child = new MyTableTranslation();
child.setSpeaker("Liotta");
parent = myTableService.saveMyTableTranslation(parent);
log.debug("parent ID : " + parent.getId());
MyTable spanishTables= myTableService.getMyTable("Spanish");
List<MyTableTranslation> spanishTranslations = spanishTables.getTranslations();
log.debug("spanishTranslations SIZE : " + spanishTranslations.size());
for (MyTableTranslation myTableTranslation : spanishTranslations) {
log.debug("myTableTranslation -> : " + myTableTranslation);
}
}
}
mytable.sql
CREATE TABLE IF NOT EXISTS my_table (
id IDENTITY PRIMARY KEY,
lang VARCHAR UNIQUE,
text VARCHAR
);
delete from my_table;
INSERT INTO my_table VALUES (1, 'Spanish', 'Beautiful...');
INSERT INTO my_table VALUES (2, 'English', 'Great...');
INSERT INTO my_table VALUES (3, 'French', 'Romantic...');
CREATE TABLE IF NOT EXISTS my_table_translation (
id INTEGER,
speaker VARCHAR
);
delete from my_table_translation;
INSERT INTO my_table_translation VALUES (1, 'Eduardo');
INSERT INTO my_table_translation VALUES (1, 'Diego');
INSERT INTO my_table_translation VALUES (2, 'George');
INSERT INTO my_table_translation VALUES (3, 'Pierre');
I tried to use #OneToMany annotation, and I change my DTO:
#Entity
#Table(name = "my_table")
public class MyTable {
#Id
#Column(name = "id", nullable = false, unique = true)
private Integer id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "id", referencedColumnName = "id")
private List<MyTableTranslation> translations = new ArrayList<MyTableTranslation>();
// getters and setters
...
}
And changed the Criteria as:
public void findAll(String isolang) {
List<MyTable> list = repository.findAll(new Specification<MyTable>() {
#Override
public Predicate toPredicate(Root<MyTable> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<Object, Object> langJoin = root.join("translations", JoinType.LEFT);
return cb.equal(langJoin.get("lang"), isolang);
}
});
}
But the list has no items. If I change the FetchType to EAGER, the Predicate has no effect, I get all the languages. I don't know how to proceed now...

Spring Data JPA Subquery with Criteria API

I am trying to create a dynamic query with Specification with two entities which have bidirectional relation. The entities are:
#Entity
#Table("SUPPLIERS")
public class Supplier implements Serializable {
#Id
Column("ID")
private Long id;
#Id
Column("COMPANY_ID")
private Long companyId;
}
#Entity
#Table("EMPLOYEES")
public class Employee implements Serializable {
#Id
private Long id;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "FIRM_ID", referencedColumnName = "ID"),
#JoinColumn(name = "FIRM_COMPANY_ID", referencedColumnName = "COMPANY_ID")
})
private Supplier supplier;
}
When I want to select employees based on their supplier,
return new Specification<Employee>() {
#Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Long[] supplierCodes = {1L, 2L};
Subquery<Supplier> supplierBasicSubquery = query.subquery(Supplier.class);
Root<Supplier> supplierBasicRoot = supplierBasicSubquery.from(Supplier.class);
Join<Employee, Supplier> sqTfV = root.join("supplier", JoinType.INNER);
supplierBasicSubquery.select(sqTfV).where(sqTfV.<Long>get("id").in(supplierCodes));
return root.<Supplier>get("supplier").in(supplierBasicSubquery);
}
};
When its executed, it generates SQL like:
SELECT ....
FROM EMPLOYEES E
INNER JOIN ....
WHERE (E.FIRM_ID, E.FIRM_COMPANY_ID) in
(SELECT (s.ID, s.COMPANY_ID) FROM SUPPLIERS WHERE SUPPLIER.ID in (1, 2))
As you can see, the inner select columns are surrounded by parenthesis which causes Oracle to throw exception:
java.sql.SQLSyntaxErrorException: ORA-00920: invalid relational operator
How can I fix this issue, any suggestions?
Thanks a lot.

How to use Plural Attributes in JPA for criteria query?

This is my root entity ArticleType from which I want to generate a query. I want to fetch a collection articleTypeVarianteOptionCollection and add some condition for that collection.
public class ArticleType extends BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "art_typ_index")
private Integer artTypIndex;
#Column(name = "art_typ_code", nullable = false)
private String artTypCode;
#OneToMany(mappedBy = "atvoIndexArticleType", fetch = FetchType.LAZY)
private Set<ArticleTypeVarianteOption> articleTypeVarianteOptionCollection;
public Integer getArtTypIndex()
{
return artTypIndex;
}
public void setArtTypIndex(Integer artTypIndex)
{
this.artTypIndex = artTypIndex;
}
public String getArtTypCode()
{
return artTypCode;
}
public void setArtTypCode(String artTypCode)
{
this.artTypCode = artTypCode;
}
#XmlTransient
public Set<ArticleTypeVarianteOption> getArticleTypeVarianteOptionCollection()
{
return articleTypeVarianteOptionCollection;
}
public void setArticleTypeVarianteOptionCollection(Set<ArticleTypeVarianteOption> articleTypeVarianteOptionCollection)
{
this.articleTypeVarianteOptionCollection = articleTypeVarianteOptionCollection;
}
}
This is my OptionArticle entity :
public class ArticleTypeOption extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ato_index")
private Integer atoIndex;
#Column(name = "ato_isremoved")
private Integer atoIsremoved;
#JoinColumn(name = "ato_index_art_type", referencedColumnName = "art_typ_index")
#ManyToOne(fetch = FetchType.LAZY)
private ArticleType atoIndexArtType;
#JoinColumn(name = "ato_index_option", referencedColumnName = "opt_art_index")
#ManyToOne(fetch = FetchType.LAZY)
private OptionArticle atoIndexOption;
public ArticleTypeOption() {
}
public ArticleTypeOption(Integer atoIndex) {
this.atoIndex = atoIndex;
}
public Integer getAtoIndex() {
return atoIndex;
}
public void setAtoIndex(Integer atoIndex) {
this.atoIndex = atoIndex;
}
public Integer getAtoIsremoved() {
return atoIsremoved;
}
public void setAtoIsremoved(Integer atoIsremoved) {
this.atoIsremoved = atoIsremoved;
}
public ArticleType getAtoIndexArtType() {
return atoIndexArtType;
}
public void setAtoIndexArtType(ArticleType atoIndexArtType) {
this.atoIndexArtType = atoIndexArtType;
}
public OptionArticle getAtoIndexOption() {
return atoIndexOption;
}
public void setAtoIndexOption(OptionArticle atoIndexOption) {
this.atoIndexOption = atoIndexOption;
}
}
My query would be :
SELECT
articleType
FROM ArticleType articleType
LEFT JOIN articleType.articleTypeVarianteOptionCollection atOption
where atOption.atoIsremoved = 0;
I have tried this for where clause in jpa :-
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> criteriaQry = criteriaBuilder.createQuery(entityClass);
Root<T> root = criteriaQry.from(entityClass);
criteriaQry.select(root).distinct(true);
for (PluralAttribute<? super T, ?, ?> pa : root.getModel().getPluralAttributes())
{
System.out.println(pa.getName());
System.out.println(pa.getCollectionType());
}
Now how to add where clause using this PluralAttribute ?
Thanks in advance.
First, let's start with the SQL query:
SELECT
articleType
FROM ArticleType articleType
LEFT JOIN articleType.articleTypeVarianteOptionCollection atOption
where atOption.atoIsremoved = 0;
Whenever you use the LEFT JOIN table in the WHERE condition, the JOIN will behave like an INNER JOIN.
So, this is how you translate this SQL query into Criteria:
Integer atoIsremoved = ...;
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<ArticleType> criteria = criteriaBuilder.createQuery(ArticleType.class);
Root<ArticleType> root = criteria.from(ArticleType.class);
criteria.select(root).distinct(true);
Join<ArticleType, ArticleTypeVarianteOption> joinOptions = root.join(
"articleTypeVarianteOptionCollection",
JoinType.LEFT
);
criteria.where(
criteriaBuilder.or(
criteriaBuilder.isNull(
joinOptions.get("id")
),
criteriaBuilder.equal(
joinOptions.get("atoIsremoved"), atoIsremoved
)
)
);
TypedQuery<ArticleType> query = entityManager.createQuery(criteria);
List<ArticleType> resultList = query.getResultList();
You may use something like this:
Fetch artTypeFetch = root.fetch("atoIndexArtType", JoinType.LEFT);
artTypeFetch.fetch("articleTypeVarianteOptionCollection", JoinType.LEFT);
To add where clause (condition), I have to use joins as specified below as joinOptions.
And to retrieve data I have to fetch those data as fetch Relation.
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<ArticleType> criteria = criteriaBuilder.createQuery(ArticleType.class);
Root<ArticleType> root = criteria.from(ArticleType.class);
criteria.select(root).distinct(true);
Join<ArticleType, ArticleTypeVarianteOption> joinOptions = root.join("articleTypeVarianteOptionCollection");
if (fetchRelations != null)
{
for (String fetchReln : fetchRelations)
{
FetchParent<ArticleType, ArticleType> fetch = root;
for (String reln : fetchReln.split("\\."))
{
FetchParent<ArticleType, ArticleType> originalFetch = fetch;
for (String childReln : reln.split(":"))
{
fetch = originalFetch.fetch(childReln, JoinType.LEFT);
}
originalFetch = fetch;
}
}
}
Predicate[] predArray = new Predicate[2];
predArray[0] = criteriaBuilder.equal(joinOptions.get("atvoIndexConfig"), configId);
predArray[1] = criteriaBuilder.equal(joinOptions.get("atvoIndexArticleType"), articleTypeId);
criteria.where(predArray);
TypedQuery<ArticleType> typedQry = entityManager.createQuery(criteria);
ArticleType articleTypeResult;
try
{
articleTypeResult = typedQry.getSingleResult();
}
catch (NoResultException ex)
{
articleTypeResult = null;
}
return articleTypeResult;
It's possible to achieve what you need using JPQL. The query is similar to the criteria solution but is more readable to me:
SELECT distinct a FROM ArticleType a
LEFT JOIN FETCH a.articleTypeViarianteOptionCollection atOption
WHERE atOption is null OR atOption.atoIsremoved=0

Categories

Resources