JPA join column only for insert/delete , disable select - jpa

I have #OneToMAny realationship inside my entity.
Is it possible to disable jpa from generating select for the joined column? beacuse I have many records in my main table and when selecting them , each record performs select for the joined column. I would like to disable this select is that possible?
UPDATE:
I tried inserting fetch LAZY but it still creates a select for Table02f and Table03f...
This is my code:
public class Table01f implements Serializable {
#OneToMany(fetch=FetchType.LAZY , cascade = CascadeType.ALL, mappedBy = "table01f")
private List<Table02f> table02fList;
//getter & setter...
}
public class Table02f implements Serializable {
#JoinColumn(name = "F2PRP", referencedColumnName = "F1PRP", insertable = false, updatable = false)
#ManyToOne(optional = false)
#JsonIgnore
private Table01f table01f;
#OneToMany(fetch=FetchType.LAZY , cascade = CascadeType.ALL, mappedBy = "table02f")
private List<Table03f> table03fList;
//getter & setter...
}
public class Table03f implements Serializable {
#JoinColumns({
#JoinColumn(name = "F3PRP", referencedColumnName = "F2PRP", insertable = false, updatable = false),
#JoinColumn(name = "F3BRN", referencedColumnName = "F2BRN", insertable = false, updatable = false)})
#ManyToOne(optional = false)
#JsonIgnore
private Table02f table02f;
//getter & setter...
}
Thank's In Advance.

Just add the fetch type LAZY to your #OneToMany relationship:
#OneToMany(fetch=FetchType.LAZY)
When you load the list of your main entities, JPA won't populate your list for this relationship, avoiding the generation of the SELECT.
Just have a look at this functionality in JPA documentation so that you can understand how to use it.

If you don't need the data make it LAZY (in general always make everything LAZY).
If you need the data, then you can use batch fetching, or join fetching.
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html

Related

How to update records that are dependent on a different table using JPA Criteria API?

I have the following entities with the one-to-one relationship:
#NoArgsConstructor
#Data
#DynamicUpdate
#Table(name = "product")
#Entity
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.UUID)
#Column(name = "id", updatable = false, nullable = false)
private UUID id;
#Column(name = "feed", length = 100, nullable = false)
private String feed;
// Omitted columns
#ToString.Exclude
#OneToOne(mappedBy = "product", cascade = CascadeType.ALL)
private PushPermission pushPermission;
}
#Data
#NoArgsConstructor
#Table(name = "push_permission")
#Entity
public class PushPermission implements Serializable {
#Id
#Column(name = "id", updatable = false, nullable = false)
private UUID id;
// Omitted columns
#ToString.Exclude
#OneToOne
#JoinColumn(name = "id")
#MapsId
private Product product;
}
I would like to update all records in PushPermission where feed (column from Product) is not equal to PROMO using JPA Criteria API.
I have used the following CriteriaUpdate:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaUpdate<PushPermission> criteriaUpdate = cb.createCriteriaUpdate(PushPermission.class);
Root<PushPermission> root = criteriaUpdate.from(PushPermission.class);
criteriaUpdate.set("exampleField", true);
Predicate selectedProductsPredicate = root.get("id").in(ids);
Predicate skipFeedPredicate = cb.notEqual(root.get("product").get("feed"), "PROMO");
criteriaUpdate.where(cb.and(selectedProductsPredicate, skipFeedPredicate));
Query query = entityManager.createQuery(criteriaUpdate);
query.executeUpdate();
but I got the following error message:
ERROR: missing FROM-clause entry for table "p2_0"
Generated update statement by Hibernate:
update
push_permission
set
exampleField=?,
where
id in(?,?)
and p2_0.feed!=?
Besides I tried to use joining:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaUpdate<PushPermission> criteriaUpdate = cb.createCriteriaUpdate(PushPermission.class);
Root<PushPermission> root = criteriaUpdate.from(PushPermission.class);
Join<PushPermission, Product> productJoin = root.join("product");
criteriaUpdate.set("exampleField", true);
Predicate selectedProductsPredicate = root.get("id").in(ids);
Predicate skipFeedPredicate = cb.notEqual(productJoin.get("feed"), "PROMO");
criteriaUpdate.where(cb.and(selectedProductsPredicate, skipFeedPredicate));
Query query = entityManager.createQuery(criteriaUpdate);
query.executeUpdate();
but I got the following message:
The root node [me.foo.app.PushPermission] does not allow join/fetch
Hibernate didn't generate any update statement.
I use Postgres SQL 14.5 and I know I can do the native query which works:
update push_permission set exampleField=true from product where push_permission.id=product.id and product.feed<>'PROMO';
but I wonder I can do it with the use of JPA Criteria API.
I use Spring Boot 3.0.2 that implies Hibernate 6.
That's not yet possible, but support for that is on the roadmap. For now, you'd have to use an exists subquery to model this i.e.
update PushPermission p
set p.exampleField=true
where exists (
select 1
from product pr
where p.id=pr.id
and pr.feed<>'PROMO';
)

JPA CriteriaBuilder JOIN not working for collection

I have two entities, in one to many relationship. I am trying to join the collection of entities, but can't wrap my head around it how to use the framework. I always used hibernate's DetachedCriteria but is not an option for me anymore, any help would be great.
#Entity
#Table(name = "Project")
public class Project implements Serializable {
....
#OneToMany(cascade = CascadeType.ALL, mappedBy = "project")
private Collection<WorkReport> workReportCollection;
....
#Data
#Entity
#Table(name = "work_report")
public class WorkReport implements Serializable {
#JoinColumn(name = "id_work_report", referencedColumnName = "id_work_report", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Project project;
And I am trying to join workReportCollection like this, but it always throws
LazyInit Exception
when accessing the field.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Project> query = builder.createQuery(Project.class);
Root<Project> project = query.from(Project.class);
Predicate idPredicate = builder.equal(project.get("idProject"), idProject);
project.joinCollection("workReportCollection", JoinType.LEFT);
query.where(idPredicate);
TypedQuery<Project> q = em.createQuery(query);
return q.getSingleResult();
Only thing that works is using fetch instead of join but it fetches all other associations as well and that is too much data.
How to write a join correctly with JPA CriteriaBuilder? OR Should I use a fetch with some projection?
The join is correct but the collection is not initialized when you access it that's why you get the LazyInitException.
You have to add fetch:
project.fetch("workReportCollection");
to advice JPA to initialize the collection after querying.

JPA Annotations - How to retrieve all entities with a specific column value

Let say I have an entity object Customer with an "OneToMany" relation to Order. I want that when ever a "Customer" get loaded, only his orders with the Id = 1234, 5678 get loaded to.
Any ideas?
#Entity
#Table(name = "Customer")
public class Customer extends TraceableJPA {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "customer_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "Customer", targetEntity = Order.class)
#Column(name = "order_id", value = {"1234","5678"} (?))
#OrderBy("isrtdate ASC")
#BatchSize(size = 20)
private List<Order> orders = new ArrayList<Order>();
Hibernate
If you use hibernate Session and its abilites , you can always use #FilterJoinTable mechanism.
Check THIS article for more information.
Yet it is not global, you have to predefine this filter and then explicitly configure Session object to use it.
JPA
JPA in its standard has NO SUCH FUNCTIONALITY, for global relations filtering.
You can always filter it in your queries : )

JPA filter entity nested list of objects

I'm Using JPA 2.1. I have 3 entities: Dr01 , Dr02 and Dr03 with the following structure:
public class Dr01 implements Serializable {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "dr01")
private List<Dr02> dr02List;
}
public class Dr02 implements Serializable {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "dr02")
private List<Dr03> dr03List;
#JoinColumn(name = "DR2CLM", referencedColumnName = "DR1CLM", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Dr01 dr01;
}
public class Dr03 implements Serializable {
#JoinColumns({
#JoinColumn(name = "DR3CLM", referencedColumnName = "DR2CLM", insertable = false, updatable = false),
#JoinColumn(name = "DR3PTFN", referencedColumnName = "DR2PTFN", insertable = false, updatable = false)})
#ManyToOne(optional = false)
private Dr02 dr02;
private elementOBJ element;
}
public class elementOBJ implements Serializable {
#Column(name = "XXX")
private int id;
#Column(name = "YYY")
private int status;
}
I want to select from Dr01 and get only the Dr03 objects that have element objects which contains a value of 1 inside the status field.
How do I retrieve dr03List filtered by it's status value? (filtered not after the select).
Thank's In Advance.
Options that may be of assistance:
Create a DB view based on status of DR03 table and map your
entity to that.
Use JPA Inheritance using status as a
DiscriminatorColumn.
If using Hibernate use the non-JPA #Where
annotation to filter the collection

Updating multiple entities with jpa 2

I need help to understand how ORM works. Here is the scenario that is very common. I have two main tables Organization and RelatedParty which have to be in many to many relation. But there is also relation_type attribute that defines what kind of relation exists between Organization and Relatedparty.
Here are my entity classes:
Organization:
#Entity
#Table(name = "organization", catalog = "...", schema = "")
#XmlRootElement
public class Organization implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "organization_id", nullable = false)
private Integer organizationId;
#Column(name = "organization_name", nullable = false)
private String organizationName;
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "organization")
private List<Organdrelatedparty> organdrelatedpartyList;
...
//getter setter methods
Organdrelatedparty: which uses composite primary key OrgandrelatedpartyPK
#Entity
#Table(name = "organdrelatedparty", catalog = "...", schema = "")
#XmlRootElement
public class Organdrelatedparty implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected OrgandrelatedpartyPK organdrelatedpartyPK;
#JoinColumn(name = "relatedParty_id", referencedColumnName = "relatedParty_id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false, cascade= {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
private Relatedparty relatedparty;
#JoinColumn(name = "orgRelation_id", referencedColumnName = "orgRelation_id", nullable = false)
#ManyToOne(optional = false)
private ParOrgrelationtype orgRelationid;
#JoinColumn(name = "organization_id", referencedColumnName = "organization_id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false)
private Organization organization;
...
//getter setter methods
OrgandrelatedpartyPK
#Embeddable
public class OrgandrelatedpartyPK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "relatedParty_id", nullable = false)
private int relatedPartyid;
#Basic(optional = false)
#NotNull
#Column(name = "organization_id", nullable = false)
private int organizationId;
...
//getter setter methods
RelatedParty: which is in unidirectional oneToMany relationship with organdRelatedParty class. In other word that relatedParty entity has no knowledge about organdRelatedParty entity that is on the other side.
#Entity
#Table(name = "relatedparty", catalog = "...", schema = "")
#XmlRootElement
public class Relatedparty implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "relatedParty_id", nullable = false)
private Integer relatedPartyid;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "firstName", nullable = false, length = 100)
private String firstName;
#Size(max = 100)
#Column(name = "lastName", length = 100)
private String lastName;
#Basic(optional = false)
#NotNull
#Column(name = "isForeign", nullable = false)
private boolean isForeign;
...
//getter setter methods
For insertion, if I persist new Organization Entity, it cascades persist activity to new OrgandrelatedParty which also cascades persist activity to new RelatedParty. So all the related entities are persisted and it works fine.
For updating, User is expected to change existing organization and relatedParty entities and also add new relatedParty to organization. So we prefer to delete all OrgandrelatedParties first and add new relatedParties and edited relatedParties again after that.
This is our method that handles updating: We pass new organization and also all new and old relatedParties as a list to method:
firs we delete all old OrgAndRelatedParties then we create again all relatedParties in list as new OrgandrelatedParties. This is main method to update organization.
public void updateOrganization(Organization newOrganization, List<Relatedparty> newShareList) throws ControlException {
try{
tx.begin();
this.updateOrgAndRelatedShares(newOrganization, newShareList);
customerController.updateOrganization(newOrganization);
tx.commit();
}catch(ControlException ex){
...
customerController's updateOrganization method does first find old Organization by find method of entity manager then copies all attributes of new organization to old then merges old organization and flush:
public void updateOrganization(Organization newOrganization)
{
Organization preOrganization = em.find(Organization.class, newOrganization.getOrganizationId);
preOrganization.setOrganizationId(newOrganization.getOrganizationId);
preOrganization.setOrganizationName(newOrganization.getOrganizationName);
em.merge(preOrganization);
em.flush();
}
here are other methods:
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void updateOrgAndRelatedShares(Organization org, List<Relatedparty> shareList) throws ControlException
{
for(Iterator<Organdrelatedparty> it = org.getOrgandrelatedpartyList().iterator(); it.hasNext();)
{
Organdrelatedparty op = it.next();
it.remove();
op.setOrganization(null);
op.setRelatedparty(null);
deleteOrgRelated(op);
}
org.getOrgandrelatedpartyList().clear();
for(Relatedparty relatedParty: shareList){
int parOrgRelationTypeId = relatedParty.getIsPerson() ? 1:2;
createOrgAndRelatedParty(org, relatedParty, parOrgRelationTypeId);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteOrgRelated(Organdrelatedparty org) throws ControlException{
try{
org = em.find(Organdrelatedparty.class, org.getOrgandrelatedpartyPK());
em.remove(org);
em.flush();
}
catch(Exception ex){
Logger.getLogger(RelatedpartyController.class.getName()).log(Level.SEVERE, null, ex);
throw new ControlException("Couln't delete org relation", ex);
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
private void createOrgAndRelatedParty(Organization org, Relatedparty relatedParty, int parOrgRelationTypeId) throws ControlException{
if(findRelatedPartyByRegNum(relatedParty.getRegisterNumber()) == null || relatedParty.getRelatedPartyid() == null){
createRelated(relatedParty);
}else{
relatedParty = updateRelatedParty(relatedParty);
}
Organdrelatedparty preOrp = new Organdrelatedparty(relatedParty.getRelatedPartyid(),
preOrp.setOrganization(org);
preOrp.setRelatedparty(relatedParty);
preOrp.setOrgRelationid(prepareOrgandRelatedPartyType(parOrgRelationTypeId));
org.getOrgandrelatedpartyList().add(preOrp);
}
And my question is when I merge organization entity with new List organdrelatedpartyList
it throws exception like this:
SEVERE: java.lang.IllegalArgumentException: Cannot merge an entity that has been removed: mn.bsoft.crasmonclient.model.Organdrelatedparty[ organdrelatedpartyPK=mn.bsoft.crasmonclient.model.OrgandrelatedpartyPK[ relatedPartyid=71, organizationId=19 ] ]
I found out that eclipseLink does persist operation first then remove operations. So I think that it tries to insert organdrelatedparty entity that has same composite id with entity which was not deleted previously from database. I flushes every time I remove old organdrelatedparties. But it doesn't help. What is the solution? Any idea guys.
I'm using jpa 2.0; eclipseLink as provider and glassfish 3.1.2
You seem to be making these a lot more complicated than they need to be.
Why don't you just remove the Organdrelatedparty that have been removed, instead of deleting all of them, then reincarnating some of them? Reincarnating objects, especially in the same transaction is normally a bad idea.
The error that is occurring is on merge() according to the code you included you are only call merge in updateOrgAndRelatedShares(), so I don't see how this object is removed at this point? Or is your code different than you show, please include the exception stack.
You updateOrganization() method is bad, it updates the objects Id, which you should never do. Also it calls merge for no reason, it already changed the object.
Also I would normally recommend using an IdClass instead of an EmbeddedId, and recommend using TABLE or SEQUENCE id generation instead if IDENTITY.