Hibernate get parent child lazy loading with addtional check - java

I have two tables Company and Employees, one-to-many mapping. Company table contains composite primary key.
I want to search from company table based on primary id but want to put an additional check on the child table.
I want to load only a particular type of employees which I will get in the request. How it can be done in Sprongboot JPA with findById("id");
class Company{
#Id
private String companyId;
#Id
private String stateId;
private String company Name;
#OneToMany(targetEntity = Employees.class, fetch = FetchType.LAZY, cascade = {
CascadeType.ALL }, mappedBy = "company")
private Set<Employees> empList;
}
class Employees{
#Id
private String id;
//foreign key
private String companyId;
//foreign key
private String stateId;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "companyId", referencedColumnName = "companyId", insertable = false, updatable = false, nullable = true),
#JoinColumn(name = "stateId", referencedColumnName = "stateId", insertable = false, updatable = false, nullable = true) })
private Company company;
private int salary;
private String type;
}

Use Filter, which is an alternative of #Where where you can set dynamic value.
Here is the sample
#FilterDef(
name = "employeeTypeFilter",
parameters = #ParamDef(name = "type", type = "string")
)
#Filter(
name = "employeeTypeFilter",
condition = "type > : type"
)
public class Employees {
}
You can enable or disable filter from your code dynamically based on your requirement.

You can use #Where for fixed type
#Where(clause = "type = 'anyEmployeeType'")
private Set<Employees> empList;
For dynamically fetch you can query in Employees repository
List<Employees> findByTypeAndCompany(String type, Company company);

Related

How to map only single column with hibernate?

I want to join a single column from another table to one of my #Entity classes:
Currently it works as follows:
#Entity
public class Product {
#Id
private long id; //autogenerated
String type; //used for mapping
#ManyToOne
#JoinColumn(name = "type", insertable = false, updatable = false)
ProductMapping mapping;
}
#Entity
public class ProductMapping {
#Id
String type;
String longname;
}
Question: how could I replace the #ManyToOne mapping to directly map to String longname?
//TODO: how to directly map to 'mapping.longname'?
#JoinColumn(name = "type", insertable = false, updatable = false)
String mapping.longname;
You can use #Formula annotation with a query like this :
#Formula("(select pm.longname from product_mapping pm where pm.COL_NAME = value)")
private String longName;
//give the column name of type from product_mapping table
OR
You can also use the below approach :
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "type", referencedColumnName = "COL_NAME", insertable = false, updatable = false)
ProductMapping mapping;
The other entity use #NaturalId annotation on the field.
#Entity
public class ProductMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
String type;
#NaturalId
#Column(name = "SOME_VALUE")
String longname;
}

how to find id of foreign key in hibernate

I have two entities viz:
State
#Entity
#Table(name = "State")
public class StateEntity {
#Column(name = "id", length = 36, nullable = false, unique = true)
private String id;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "InsurerId", nullable = false)
private InsurerEntity insurer;
#Column(name ="StateName", length = 50, nullable = false)
private String stateName;
//getters and setters
}
Insurer
#Entity
#Table(name = "Insurer")
public class InsurerEntity {
#Column(name = "InsurerId", length = 36, nullable = false, unique = true)
private String insurerId;
#Column(name = "InsurerName", length = 100, nullable = true)
private String insurerName;
#OneToMany(mappedBy = "state", fetch = FetchType.LAZY)
private List<StateEntity> stateEntityList;
//getters and setters
}
the insurer's id gets saved in state database and I want to retrieve it using hibernate query but I cant't seem to find the solution for that
How to write this query SELECT InsurerId FROM State; in Hibernate query using CriteriaBuilder, CriteriaQuery and Root..
If you want to select all Insurers's Ids for all states:
String selectionQuery = "SELECT s.insurer.insurerId FROM State s";
List<String> insurersIds = session.createQuery(selectionQuery).list();
If you want to select the Insurer's Id of a certain state:
String selectionQuery = "SELECT s.insurer.insurerId FROM State s WHERE s.id = :stateId";
String insurerId = (String) session.createQuery(selectionQuery).setParameter("stateId", stateId).getSingleResult(); //This should be placed in a try/catch block to handle org.hibernate.NonUniqueResultException
Edit:
You should update your Insurer entity as Prasad wrote in his answer.
for this you have to map both the class as in put #oneToMany annotation in class InsurerEntity as well
#OneToMany(fetch = FetchType.LAZY,mappedBy="StateEntity", cascade = CascadeType.ALL)
private List< StateEntity > StateEntitys;
and when you fetch states you will also get object of InsurerEntity in it from where you can access it with the getter

"MultipleBagFetchException: cannot simultaneously fetch multiple bags" when joining three depth table

org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously
fetch multiple bags: [Order.items, OrderItem.options];
Above is an exception i faced when i join three tables like below.
OrderItemOption.java
#Entity
public class OrderItemOption {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_option_id")
private Long id;
#Column(name = "item_id", nullable = false)
private Long itemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "item_id",
referencedColumnName = "item_id",
insertable = false,
updatable = false
)
private OrderItem orderItem;
}
OrderItem.java
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long id;
#Column(name = "order_id", nullable = false)
private Long orderId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "order_id",
referencedColumnName = "order_id",
insertable = false,
updatable = false,
nullable = false
)
private Order order;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "orderItem")
#OrderBy("item_option_id ASC")
private List<OrderItemOption> options;
}
Order.java
#Entity
public class Order {
#Id
#Column(name = "order_id", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
#OrderBy("item_id ASC")
private List<OrderItem> items;
}
And here's my QueryDSL code to join them at one time.
final QOrder order = QOrder.order;
final QOrderItem item = QOrderItem.orderItem;
final QOrderItemOption option = QOrderItemOption.orderItemOption;
from(order)
.leftJoin(order.items, item).fetchJoin()
.leftJoin(item.options, option).fetchJoin()
.where(
order.id.eq(orderId)
.and(item.id.in(itemIds))
.and(option.id.in(optionIds))
)
.fetchOne())
What i'm trying to do is to get Order object which contains filtered relationship, so that i can access filtered children via order object.
and the type of relationship should be a List, not a Set.
for example, order.getItems().get(0).getOptions.get(0)
How can i achieve that goal?
To avoid above exception there are two possibilities:
Change List to Set
or
Use List but do not fetch two bags. This means don't use fetchJoin() on both collections.
Filtering:
Using where conditions collections will be not filtered. Collections will contain all associated objects. Joining in JPA is for creating conditions on root object - Order. It is not the same as in SQL.
It is possible to filter associated collections using JPA 2.1 JOIN ON feature. This allows additional conditions in ON clause
see for example QueryDSL Left Join with additional conditions in ON
If you really can not use Set instead of List:
Parent.class
#OneToMany(
mappedBy = "parent",
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.MERGE }
)
#OrderColumn(name = "position")
private List<Child> childs = new ArrayList<>();
Child.class
#ManyToOne(fetch = FetchType.LAZY)
private Parent parent;
And create a column in the Child's table named e.g "position"
ALTER TABLE child ADD COLUMN position integer NOT NULL default 0
And if you can not use other column in table, them you need to query the lists in sequence. Or use the id of the child and a custom getter.
#OrderColumn(name = "id_child", updatable = false, insertable = false)
public List<Child> getChilds() {
childs.removeAll(Collections.singleton(null));
return childs;
}

eclipselink manytoone not save

i have a persit problem with elcipselink
here is my model :
#Entity
#Table(name = "TBL_ASSOC_LANGUE_CODE_BE_GARE")
public class AssocLangueCodeBeGare {
#AttributeOverrides({ #AttributeOverride(name = "id_langue", column = #Column(name = "ID_LANGUE") ),
#AttributeOverride(name = "id_gare", column = #Column(name = "ID_GARE") ) })
#EmbeddedId
private AssocLangueCodeBeGareFK key;
#Column(name = "CODE_BE", length = 4)
private String codeBe;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "ID_GARE", referencedColumnName = "ID_GARE", insertable = false, updatable = false)
private StopPoint stopPoint;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_LANGUE", referencedColumnName = "ID_LANGUE", insertable = false, updatable = false)
private Langue langue;
in StopPoint i don t reference table AssocLangueCodeBeGare, i don t need it.
then when i do :
this.serviceStopPoint.save(currentStopPoint);
for (AssocLangueCodeBeGare assoc : listAssocLangueCodeBeGare) {
assoc.setStopPoint(currentStopPoint);
this.serviceAssocLangueCodeBeGare.save(assoc);
}
save is
#Override
public void save(T entityToSave) {
this.getEntityManager().persist(entityToSave);
}
I m using batch insert for writing and sometimes when i save another entity flush is done and i get :
Caused by: org.h2.jdbc.JdbcBatchUpdateException: NULL not allowed for column "ID_GARE"; SQL statement:
INSERT INTO TBL_ASSOC_LANGUE_CODE_BE_GARE (CODE_BE, ID_GARE, ID_LANGUE) VALUES (?, ?, ?) [23502-192]
at org.h2.jdbc.JdbcPreparedStatement.executeBatch(JdbcPreparedStatement.java:1208)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.executeBatch(DatabasePlatform.java:2134)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeJDK12BatchStatement(DatabaseAccessor.java:871)
does i miss something in the mapping?
does i need to add something like this in stoppoint entity:
#OneToMany(mappedBy = "stopPoint", cascade = CascadeType.ALL)
List<AssocLangueCodeBeGare> assocLangueCodeBeGares;
#EDIT1
I think its caused by my EmbeddedId because it was null!! the mapping does'nt set correct value in the embedable object?
here is the embedable object :
#Embeddable
public class AssocLangueCodeBeGareFK implements Serializable {
private String id_langue;
private Long id_gare;
#Override
public int hashCode() {
thanks a lot!
The insertable = false and updatable = false prevent the value from the mapping from being written to the database. You need to have another mapping to that database column with the value set, or it won't get into the database. While making your OneToMany the ID works, so would the MapsId annotation in the current code as JPA will then set the value in your embeddable ID. Or you could have manually added the value from the referenced class into the appropriate AssocLangueCodeBeGare.key fields.
Finaly i find a solution :
I use #IdClass(AssocLangueCodeBeGareFK.class on AssocLangueCodeBeGare
And i put #id on
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "ID_GARE", referencedColumnName = "ID_GARE")
private StopPoint stopPoint;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_LANGUE", referencedColumnName = "ID_LANGUE")
private Langue langue;
I rename on my AssocLangueCodeBeGareFK class name of my property to be the same as the AssocLangueCodeBeGare class :
private Long stopPoint;
private String langue;
now all work like a charm.
if it helps...

Joining tables in hibernate

I have two tables
Customer Rate
------------- -----------
res_number product
strategy name
fname type
lname rate
..... ......
And I created two beans
1. 2.
#Entity #Entity
#Table(name="customer") #Table(name="rates")
EmployeeDetails{ CardDetails{
#Col(name="res_number") #col(name="product")
String resNum; String product;
..... ....
} }
Now the query I have is
hql = "from CardDetails cd, EmployeeDetails ed where ed.strategy = cd.product".
But it gives me reference problems saying hibernate.QueryException: could not resolve property:
I have tried adding
#OneToOne(mappedBy = "strategy")
#Cascade(value = CascadeType.ALL)
private EmployeeDetails empDetails;
in CardDetails but it gives me error saying no OneToOne is possible ani... tried changing to ManyToOne and OneToMany but doesnot work. Can anyone please tell me how to map the beans for join using annotations? Note: The database is not designed correctly and there is no common field in both tables(like a foreign key). So any help is greatly appreciated.
EDIT:
Adding the beans:
#Entity
#Table(name="rates")
public class CardDetails {
#Id
#Column(name="CARD_NAME")
String cardName;
#Column(name="CARD_TYPE")
String cardType;
#Column(name="FAQ_PAGE")
String faqPage;
#Column(name="GEN_INTRO_DISCL")
String genIntroDiscl;
#Column(name="GEN_REGULAR_RATE")
String genRegularRate;
#Column(name="BT_FEE")
String btFee;
#Column(name="BONUS")
String bonus;
#Column(name="ART_WORK")
String artWork;
#Column(name="DISCLOSURE_LINK")
String disclosureLink;
#Column(name="TERMS_LINK")
String termsLink;
#Column(name="PRODUCT")
String product;
#Entity
#Table(name="CUSTOMER")
#SecondaryTable(name="APPLICANT")
public class EmployeeDetails {
#Id
#Column(name="RESERVATION_NUMBER")
String reservNumber;
#Column(name="SSN")
int ssnNumber;
#Column(name="BANK_NUMBER")
String bankNumber;
#Column(name="BRANCH_NUMBER")
String branchNumber;
#Column(name="EMPLOYEE_ID")
String empId;
#Column(name="STRATEGY")
String strategy;
From the looks of your HQL, you'd like to join the two tables using stratety in the Customer table and product in the Rate table. This would imply that strategy is a foreign key.
If this is indeed a one-to-one relationship, then inside of CardDetails, try this:
#OneToOne
#JoinColumn(name = "product", referencedColumnName = "strategy")
private EmployeeDetails employeeDetails;
This assumes you don't already have product mapped as a property in CardDetails, if you do, you'll need to do it this way, otherwise Hibernate will complain about duplicate field mappings.
#Column(name = "product", columnDefinition = "char")
private String product;
#OneToOne
#JoinColumn(name = "product", referencedColumnName = "strategy", insertable = false, updatable = false)
private EmployeeDetails employeeDetails;
If it needs to be a one-to-many relationship, then do it this way:
#Column(name = "product", columnDefinition = "char")
private String product;
#OneToMany
#JoinColumn(name = "product", referencedColumnName = "strategy", insertable = false, updatable = false)
private List<EmployeeDetails> employeeDetails;

Categories

Resources