JPA query with left join and "not exists" - jpa

I am writing a JPA query using TopLink which involves the following three entities.
#Entity
#Table(name = "OFFERS")
public class Offers implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="offers_seq_gen")
#SequenceGenerator(name="offers_seq_gen", sequenceName="OFFERS_SEQ")
#Basic(optional = false)
#Column(name = "OFFERID")
private Long offerid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "offers", fetch = FetchType.LAZY)
private List<Coupons> couponsList;
}
#Entity
#Table(name = "COUPONS")
public class Coupons implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="coupons_seq_gen")
#SequenceGenerator(name="coupons_seq_gen", sequenceName="COUPONS_SEQ")
#Basic(optional = false)
#Column(name = "COUPONID")
private Long couponid;
#Basic(optional = false)
#Column(name = "ISSUED", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
private Date issued;
#JoinColumn(name = "USERID", referencedColumnName = "USERID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Users users;
#JoinColumn(name = "OFFERID", referencedColumnName = "OFFERID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Offers offers;
#Entity
#Table(name = "USERS")
public class Users implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="users_seq_gen")
#SequenceGenerator(name="users_seq_gen", sequenceName="USERS_SEQ")
#Basic(optional = false)
#Column(name = "USERID")
private Long userid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "users", fetch = FetchType.LAZY)
private List<Coupons> couponsList;
I need to find all the Offers who either have no coupons for a given user or all the coupons for the user were issued more than a day ago.
I have tried many different approaches and the only query I have come up with so far, which does not crash the server on deployment is:
SELECT o
FROM Offers o
LEFT JOIN o.couponsList c
WHERE
c.users.userid = :userid AND c.issued < :yesterday
OR
NOT EXISTS
(SELECT c1
FROM Coupons c1
WHERE c1.offers = o AND c1.users.userid = :userid)
But it does not return the Offer when the Coupons entry does not exist.

I managed to find a working query. Leaving it here for reference if anyone had similar issues:
SELECT o FROM Offers o WHERE
NOT EXISTS
(SELECT c FROM Coupons c WHERE c.users.userid = :userid
AND c.issued > :yesterday AND c.offers = o)
OR NOT EXISTS
(SELECT c1 FROM Coupons c1 WHERE c1.offers = o
AND c1.users.userid = :userid)

Related

JPA cascade - deletion is not working when id is different in join table

I am using spring JPA to insert and delete the records in these tables. I can able to insert the record, but deletion is not happening due to different id's in emp_workstation table. If both employee_id and workstation_id were same then delete is working fine, but if both the id's were not same then delete not happening in employee table and emp_workstation table (cascade has set to 'ALL'). Does anybody face this problem before or know how to solve this issue?
Employee.java
#Setter
#Getter
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PUBLIC)
#NoArgsConstructor(access = AccessLevel.PUBLIC)
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_employee_id")
#SequenceGenerator(name = "sq_employee_id", sequenceName = "sq_employee_id", allocationSize = 1)
private Long id;
#Column(name = "employee_name", length = 50)
private String name;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "emp_workstation", joinColumns = {
#JoinColumn(name = "employee_id") }, inverseJoinColumns = { #JoinColumn(name = "workstation_id") })
private Workstation workstation;
}
Workstation.java
#Setter
#Getter
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PUBLIC)
#NoArgsConstructor(access = AccessLevel.PUBLIC)
#Entity
#Table(name = "workstation")
public class Workstation {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_workstation_id")
#SequenceGenerator(name = "sq_workstation_id", sequenceName = "sq_workstation_id", allocationSize = 1)
private Long id;
#Column(name = "workstation_name", nullable = false, length = 100)
private String name;
#Column(name = "workstation_area", nullable = false, length = 100)
private String name;
#OneToOne(mappedBy = "workStation")
private Employee employee;
}
Example: In EMP_WORKSTATION table, if EMPLOYEE_ID and WORKSTATION_ID were same then deletion is working fine. but if both id were different then deletion is not working.
Thanks

Netbeans wizard Entity Classes from Database, not all tables mapped

I used this wizard to create entity classes from my database. Some tables have not been transformed into classes, but there are attributes that identify the relationships.
this is my db ERD (mysql)
and this is the user entity class (attributes)
#Entity
#Table(name = "user")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
#NamedQuery(name = "User.findByOid", query = "SELECT u FROM User u WHERE u.oid = :oid"),
#NamedQuery(name = "User.findByUsername", query = "SELECT u FROM User u WHERE u.username = :username"),
#NamedQuery(name = "User.findByPassword", query = "SELECT u FROM User u WHERE u.password = :password"),
#NamedQuery(name = "User.findByEmail", query = "SELECT u FROM User u WHERE u.email = :email"),
#NamedQuery(name = "User.findByAddress", query = "SELECT u FROM User u WHERE u.address = :address"),
#NamedQuery(name = "User.findBySince", query = "SELECT u FROM User u WHERE u.since = :since")})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "oid")
private Integer oid;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 15)
#Column(name = "username")
private String username;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 15)
#Column(name = "password")
private String password;
// #Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "email")
private String email;
#Size(max = 50)
#Column(name = "address")
private String address;
#Basic(optional = false)
#NotNull
#Column(name = "since")
#Temporal(TemporalType.DATE)
private Date since;
#JoinTable(name = "favorite", joinColumns = {
#JoinColumn(name = "user_oid", referencedColumnName = "oid")}, inverseJoinColumns = {
#JoinColumn(name = "wheelchair_oid", referencedColumnName = "oid")})
#ManyToMany
private List<Wheelchair> wheelchairList;
#ManyToMany(mappedBy = "userList1")
private List<Wheelchair> wheelchairList1;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "senderOid")
private List<Comment> commentList;
#JoinColumn(name = "role_oid", referencedColumnName = "oid")
#ManyToOne(optional = false)
private Role roleOid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOid")
private List<Orthopedy> orthopedyList;
public User() {
}
...
i can't understand something:
where is the OWN join table?
why i have userList1 and wheelchairList1? should it identifies OWN table? in this case i can rename it here or i have to rename it in some xml file?
why of
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOid")
private List<Orthopedy> orthopedyList;
?
it should be OneToOne...
moreover the "JSF from entities class" wizard creates CRUD operation to manage Users, how can i manage join tables? I need to write something in the controller like what?
can you please link me some resource where i can learn this?
thank you so much
While Creating Entities It Creates Classes For All Tables With Primary Key
But not for tables that have many to many relations . its managed by their parent classes it is maintained as a list.
This is my code for managing my many to many table of SubjectFaculty which has details of Faculty and Subjects
Assigning A Subject To Faculty
public void assignFacultyToSubject(String facultyUname, Integer subjectId) {
try {
Subject oSubject = em.find(Subject.class, subjectId);
Faculty oFaculty = em.find(Faculty.class, facultyUname);
College oCollege = em.find(College.class, oFaculty.getCollegeUname().getCollegeUname());
List<Faculty> lstFaculty = oSubject.getFacultyList();
List<Subject> lstSubject = oFaculty.getSubjectList();
if (!lstSubject.contains(oSubject)) {
lstFaculty.add(oFaculty);
lstSubject.add(oSubject);
oSubject.setFacultyList(lstFaculty);
oFaculty.setSubjectList(lstSubject);
em.merge(oSubject);
em.getEntityManagerFactory().getCache().evictAll();
} else {
System.out.println("Entry Already Found");
}
} catch (Exception e) {
System.out.println("Error :- " + e.getMessage());
}
}
Removing Subject And Faculty Details Form Many to Many Table
#Override
public void removeFacultySubject(String facultyUname, Integer subjectId) {
try {
Subject oSubject = em.find(Subject.class, subjectId);
Faculty oFaculty = em.find(Faculty.class, facultyUname);
List<Subject> lstSubject = oFaculty.getSubjectList();
List<Faculty> lsFaculty = oSubject.getFacultyList();
lstSubject.remove(oSubject);
lsFaculty.remove(oFaculty);
em.merge(oSubject);
} catch (Exception e) {
System.out.println("Error :- " + e.getMessage());
}
}

query with OneToMany - openJPA vs EclipseLink

openjpa is complaining about an incorrect argument for a JPA query that EclipseLink properly handles. EclipseLink returns the set of validation messages for the motor.
Two questions:
1) Is my query wrong and EclipseLink is kindly handling it anyway?
2) Any suggestions on how to restructure the query for openjpa?
Thanks for thinking about my question!
Query
SELECT m.valMessages FROM ThreePhaseMotorInput m WHERE m.id = :id
Actual openjpa exception
Caused by: <openjpa-2.3.0-r422266:1540826 nonfatal user error> org.apache.openjpa.persistence.ArgumentException:
Query projections cannot include array, collection, or map fields.
Invalid query: "SELECT m.valMessages FROM ThreePhaseMotorInput m WHERE m.id = :id"
at org.apache.openjpa.kernel.ExpressionStoreQuery$AbstractExpressionExecutor.assertNotContainer(ExpressionStoreQuery.java:328)
at org.apache.openjpa.kernel.ExpressionStoreQuery$DataStoreExecutor.<init>(ExpressionStoreQuery.java:770)
at org.apache.openjpa.kernel.ExpressionStoreQuery.newDataStoreExecutor(ExpressionStoreQuery.java:179)
at org.apache.openjpa.kernel.QueryImpl.createExecutor(QueryImpl.java:749)
at org.apache.openjpa.kernel.QueryImpl.compileForDataStore(QueryImpl.java:707)
at org.apache.openjpa.kernel.QueryImpl.compileForExecutor(QueryImpl.java:689)
at org.apache.openjpa.kernel.QueryImpl.compile(QueryImpl.java:589)
at org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:1038)
at org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:1017)
ThreePhaseMotorInput mapping
public class ThreePhaseMotorInput implements IThreePhaseMotorInput, Serializable {
private static final long serialVersionUID = 8084370807289186987L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Integer version;
private Integer status;
#OneToOne(cascade = CascadeType.ALL, optional = true, targetEntity = UnapprovedThreePhaseMotor.class)
#JoinColumn(name = "unapproved_id")
private IThreePhaseMotor unapprovedMotor;
#OneToOne(cascade = CascadeType.ALL, optional = true, targetEntity = ApprovedThreePhaseMotor.class)
#JoinColumn(name = "approved_id")
private IThreePhaseMotor approvedMotor;
#OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = ValidationMessage.class)
#JoinColumn(name = "input_id", referencedColumnName = "id", nullable = false)
#OrderColumn(name = "idx")
private List<IValidationMessage> valMessages;
ValidationMessage mapping
public class ValidationMessage implements Serializable, IValidationMessage {
private static final long serialVersionUID = 8765213112015434057L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "record_id")
private Long recordId;
#Column(name = "field_name")
private String fieldName;
#Column(name = "validation_msg")
private String validationMsg;
private Integer status;
#Column(name = "fail_field")
private String failField;
#Column(name = "error_source")
private Integer errorSource;
Check http://docs.oracle.com/javaee/6/tutorial/doc/bnbuf.html#bnbvx - SELECT clause: A SELECT clause cannot specify a collection-valued expression. For example, the SELECT clause p.teams is invalid because teams is a collection.
But you can use valMessages for INNER/OUTER join and select IValidationMessage entities trough it, e.g.:
SELECT ivm
FROM ThreePhaseMotorInput tpmi
INNER JOIN tpmi.valMessages ivm
WHERE tpmi.id = :id

Retrieving selected columns based on a many-to-many relationship expressed along with an extra column in the join table using JPA criteria

I have three tables in MySQL database.
zone_table
zone_id (PK)
zone_name
transporter_id (FK references the transporter table - unrelated here).
weight
weight_id (PK)
weight
zone_charge
zone_id (FK refernces zone_table) |
weight_id (FK references weight) | composite primary key.
charge | extra column in join.
Since a many-to-many relationship between zone_table and weight is expressed by the zone_charge table with an extra column in it (which is charge), an embeddable class ZoneChargePK representing the composite primary key has been created.
With this relationship, I need to retrieve a list of rows consisting of three fields, weight_id and weight from the weight table and charge from the zone_charge table for a given zone.
The native SQL would be as follows.
SELECT w.weight_id, w.weight, zc.charge
FROM weight w
LEFT OUTER JOIN zone_charge zc ON w.weight_id=zc.weight_id
WHERE zc.zone_id=?
ORDER BY w.weight ASC
The corresponding JPQL would be as under.
SELECT w.weightId, w.weight, zc.charge
FROM Weight w
LEFT JOIN w.zoneChargeSet zc
WITH zc.zone.zoneId=:id
ORDER BY w.weight
I would like the same thing to be expressed by JPA criteria query, since this query would, in turn be generated dynamically. I have left with the following incomplete criteria query.
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createTupleQuery();
Root<Weight> root = criteriaQuery.from(entityManager.getMetamodel().entity(Weight.class));
SetJoin<Weight, ZoneCharge> join = root.join(Weight_.zoneChargeSet, JoinType.LEFT);
criteriaQuery.multiselect(root.get(Weight_.weightId), root.get(Weight_.weight), join.get(ZoneCharge_.zoneTable));
TypedQuery<Tuple> typedQuery = entityManager.createQuery(criteriaQuery);
List<Tuple> tuples = typedQuery.getResultList();
But this results in an inner join between zone_table and zone_charge in addition to a left join between zone_charge and weight. The generated SQL query looks like the following.
select
weight0_.weight_id as col_0_0_,
weight0_.weight as col_1_0_,
zonecharge1_.zone_id as col_2_0_,
zonetable2_.zone_id as zone1_34_,
zonetable2_.transporter_id as transpor3_34_,
zonetable2_.zone_name as zone2_34_
from
social_networking.weight weight0_
left outer join
social_networking.zone_charge zonecharge1_
on weight0_.weight_id=zonecharge1_.weight_id
inner join
social_networking.zone_table zonetable2_
on zonecharge1_.zone_id=zonetable2_.zone_id
It should actually be,
select
weight0_.weight_id as col_0_0_,
weight0_.weight as col_1_0_,
zonecharge1_.charge as col_2_0_
from
social_networking.weight weight0_
left outer join
social_networking.zone_charge zonecharge1_
on weight0_.weight_id=zonecharge1_.weight_id
Except for where and order by. So how would the actual criteria query look like?
EDIT :
The ZoneTable entity (only if needed):
#Entity
#Table(name = "zone_table", catalog = "social_networking", schema = "", uniqueConstraints = {
#UniqueConstraint(columnNames = {"zone_id"})})
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "ZoneTable.findAll", query = "SELECT z FROM ZoneTable z"),
#NamedQuery(name = "ZoneTable.findByZoneId", query = "SELECT z FROM ZoneTable z WHERE z.zoneId = :zoneId"),
#NamedQuery(name = "ZoneTable.findByZoneName", query = "SELECT z FROM ZoneTable z WHERE z.zoneName = :zoneName")})
public class ZoneTable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "zone_id", nullable = false)
private Long zoneId;
#Column(name = "zone_name", length = 45)
private String zoneName;
#JoinColumn(name = "transporter_id", referencedColumnName = "transporter_id")
#ManyToOne(fetch = FetchType.LAZY)
private Transporter transporterId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "zoneTable", fetch = FetchType.LAZY)
private Set<ZoneCharge> zoneChargeSet; //<--------------------------
#OneToMany(mappedBy = "zoneId", fetch = FetchType.LAZY)
private Set<Country> countrySet;
}
The Weight entity:
#Entity
#Table(name = "weight", catalog = "social_networking", schema = "", uniqueConstraints = {
#UniqueConstraint(columnNames = {"weight_id"})})
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Weight.findAll", query = "SELECT w FROM Weight w"),
#NamedQuery(name = "Weight.findByWeightId", query = "SELECT w FROM Weight w WHERE w.weightId = :weightId"),
#NamedQuery(name = "Weight.findByWeight", query = "SELECT w FROM Weight w WHERE w.weight = :weight")})
public class Weight implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "weight_id", nullable = false)
private Long weightId;
#Column(name = "weight", precision = 35, scale = 2)
private BigDecimal weight;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "weight", fetch = FetchType.LAZY)
private Set<ZoneCharge> zoneChargeSet; //<-------------------------
}
The ZoneCharge entity:
#Entity
#Table(name = "zone_charge", catalog = "social_networking", schema = "")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "ZoneCharge.findAll", query = "SELECT z FROM ZoneCharge z"),
#NamedQuery(name = "ZoneCharge.findByZoneId", query = "SELECT z FROM ZoneCharge z WHERE z.zoneChargePK.zoneId = :zoneId"),
#NamedQuery(name = "ZoneCharge.findByWeightId", query = "SELECT z FROM ZoneCharge z WHERE z.zoneChargePK.weightId = :weightId"),
#NamedQuery(name = "ZoneCharge.findByCharge", query = "SELECT z FROM ZoneCharge z WHERE z.charge = :charge")})
public class ZoneCharge implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected ZoneChargePK zoneChargePK;
#Column(name = "charge", precision = 35, scale = 2)
private BigDecimal charge;
#JoinColumn(name = "zone_id", referencedColumnName = "zone_id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private ZoneTable zoneTable;
#JoinColumn(name = "weight_id", referencedColumnName = "weight_id", nullable = false, insertable = false, updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Weight weight;
The ZoneChargePK entity:
#Embeddable
public class ZoneChargePK implements Serializable {
#Basic(optional = false)
#Column(name = "zone_id", nullable = false)
private long zoneId;
#Basic(optional = false)
#Column(name = "weight_id", nullable = false)
private long weightId;
}
According to this relationship, the JPQL query as shown above works correctly.
It is not a social networking project. It was just originally intended. Therefore it was named such. It is about a shopping site.

query entity with condition on ManyToMany relation

I have two Entites
#Entity
public Report()
#Id
#Column(name = "REPORT_ID")
private long id;
#JsonIgnore
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name="reports_projects",
joinColumns={#JoinColumn(name="report_id", referencedColumnName="REPORT_ID")},
inverseJoinColumns={#JoinColumn(name="project", referencedColumnName="PROJECT_ID")})
private List<Project> projects;
second is:
#Entity(name = "projects")
public class Project
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PROJECT_ID")
// seems like spring's jpa has issue hanlde "_" between the words
private long id;
#Column(name = "CODE", nullable = false)
private String code;
#Column(name = "DESCRIPTION", nullable = false)
private String description;
#Column(name = "CREATION_DATE", nullable = false)
private Date creationDate;
i'm tring to query reports by projects.code
tried few stuff like
#Query("select reports from org.jpp.domain.quicksearch.ReportQS reports inner join reports.projects p where p.code in :code")
And
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<QuickSearchResult> query = cb.createQuery(QuickSearchResult.class);
Metamodel m = em.getMetamodel();
EntityType<ReportQS> ReportQSMetaModel = m.entity(ReportQS.class);
Root<ReportQS> reportsQS = query.from(ReportQS.class);
Root<Project> projects = query.from(Project.class);
Join<ReportQS, Project> joinReportsProjects = reportsQS.join("projects");
Predicate condition = cb.equal(projects.get("code"),"gnrl");
query.select(reportsQS).where(condition);
TypedQuery<QuickSearchResult> q = em.createQuery(query);
I get empty result for both of the queries
Any idea how to get this to work ?
Thanks in advance,
Oak
Try following code:
String query = "select r from ReportQS r join r.projects p where p.code = :code";
List<ReportQS> reports = em.createQuery(query,ReportQS.class).setParameter("code","grnl").getResultList();
Make sure that ReportQS is name of entity class (in your sample code you have different class name and different entity name used in query).