EntityManagerSetupException for multiple joins and a sub query for NamedQuery - jpa

I am trying to write a NamedQuery with multiple joins and one sub-Query.
These are my Entities (I have removed most of the columns so that Question doesn't get too long and reader wouldn't need to go through unnecessary columns)
#Entity
public class progressEntry implements Serializable {
#Id
#GeneratedValue
private int id;
}
#Entity
public class KnowledgeTransfer implements Serializable {
#Id
#GeneratedValue
private int id;
#ManyToOne
private progressEntry pEntry;
}
#Entity
public class ColleagueActivity implements Serializable {
#Id
#GeneratedValue
private int id;
#ManyToOne
private KnowledgeTransfer knowledgeTransfer;
#ManyToOne
private College college;
}
#Entity
public class College implements Serializable {
#Id
#GeneratedValue
private Integer id;
private Integer studentNumber;
}
When I wrote the Native Query for this one, it works fine but I was wanted to learn how to write a Named Query with this one.
Here is what I tried which throws an Exception:
select progressEntry p where p.id in (select kt.progressEntryId from KnowledgeTransfer kt join ColleagueActivity cc on cc.ktid=kt.id join College c on c.id=cc.colleagueId where c.student.studentNumber= :studentNumber)
Please let me know if there is any more information that you would like me to post.
Thanks in advance.

Related

Many To Many Relationship JPA with Entity

I have an issue trying to generate multiple relationship in JPA with three Entities.
Order
Product
Modifier
I have an Entity to handle the relationship many to many.
OrderProducts (order_id and product_id)
Contains the relationship of one order can have multiple products
OrderDetails (order_products_id and modifier_id)
Contains the id of the previous relationship Order-Products and the Id of the modifier which is a set of multiple values that can affect the price of the product.
Not quite sure how to handle this kind of relationship in JPA as I'm new to it.
You need a join entity with a composite key. You will need to research it further.
Your entities:
#Entity
#Table(name = "ordertable")
#Data
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "order")
#EqualsAndHashCode.Exclude
private Set<OrderProductModifier> products;
}
#Entity
#Table(name = "product")
#Data
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#EqualsAndHashCode.Exclude
private BigDecimal unitPrice;
}
#Entity
#Table(name = "modifier")
#Data
public class Modifier {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#EqualsAndHashCode.Exclude
private BigDecimal modifier;
}
And the entity that ties it all together will need to have the foreign keys for each of the above entities, as you have noted.
#Entity
#Table(name = "orderproductmodifier")
#Data
public class OrderProductModifier {
#EmbeddedId
private OrderProductModifierId id;
#MapsId("orderId")
#ManyToOne
#EqualsAndHashCode.Exclude
#ToString.Exclude
private Order order;
#MapsId("productId")
#ManyToOne
#EqualsAndHashCode.Exclude
private Product product;
#MapsId("modifierId")
#ManyToOne
#EqualsAndHashCode.Exclude
private Modifier modifier;
}
#SuppressWarnings("serial")
#Embeddable
#Data
public class OrderProductModifierId implements Serializable {
private Long orderId;
private Long productId;
private Long modifierId;
}
This is pretty simple to use:
private void run() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("UsersDB");
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Product product = new Product();
product.setUnitPrice(BigDecimal.TEN);
em.persist(product);
Modifier modifier = new Modifier();
modifier.setModifier(new BigDecimal(".90"));
em.persist(modifier);
Order order = new Order();
em.persist(order);
OrderProductModifier opm = new OrderProductModifier();
opm.setId(new OrderProductModifierId());
opm.setOrder(order);
opm.setProduct(product);
opm.setModifier(modifier);
em.persist(opm);
em.getTransaction().commit();
em.clear();
Order o = em.createQuery("select o from Order o join fetch o.products where o.id = 1", Order.class).getSingleResult();
System.out.println("Order for " + o.getProducts());
System.out.println("Order cost " + o.getProducts().stream().map(p->p.getProduct().getUnitPrice().multiply(p.getModifier().getModifier()).doubleValue()).collect(Collectors.summingDouble(Double::doubleValue)));
}
The above query could be better, but that will give you something to work on.

Query for joins in Spring JPA

I have the below entities
#Entity
#Getter
#Setter
public class Aggregate {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "aggregate")
private Set<Single> singleSet;
}
#Entity
#Getter
#Setter
public class Single {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
private Integer number;
#ManyToOne
#JoinColumn(name = "agg_id")
private Aggregate aggregate;
}
I also have the below repository
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
}
I want to return all associated Single records where number in object Single is equal to some random number
I am assuming that the query will be something like this
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
public List<Single> findBySingleSet_Number(Integer number);
}
However when I try to use Intellij to complete my named query it always populates like this
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
public List<Single> findBySingleSet_Empty_Number(Integer number);
}
I am wondering what the Empty stands for ?
Also should I create another Single repository since the query is related to returning Single records.

JPA - Join three tables. One with PK. The other two each have a part of the PK

I have three entities:
Customer
It has a composite PK of... customer_id and company_id
Data
ID: data_id
FK: area_id (From Area below)
FK: customer_id (From Customer above)
Area
ID: area_id
FK: company_id (From Customer above)
How do I create the #Join annotations in JPA? I assume I have to use #JoinTable, but I don't know how to do it.
Customer
#Entity
#Table(name="customer")
#NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private CustomerPK id;
//bi-directional many-to-one association to CustomColumnDataCustomer
#OneToMany(mappedBy="customer")
private List<CustomColumnDataCustomer> customColumnDataCustomers;
CustomerPK
#Embeddable
public class CustomerPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="customer_id")
private long customerId;
#Column(name="company_id")
private String companyId;
CustomColumnDataCustomer
#Entity
#Table(name="custom_column_data_customer")
#NamedQuery(name="CustomColumnDataCustomer.findAll", query="SELECT c FROM CustomColumnDataCustomer c")
public class CustomColumnDataCustomer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="custom_column_data_cust_uid")
private int customColumnDataCustUid;
//bi-directional many-to-one association to Customer
#ManyToOne
private Customer customer;
//bi-directional many-to-one association to AreaXCustomColumn
#ManyToOne
#JoinColumn(name="area_x_custom_column_uid")
private AreaXCustomColumn areaXCustomColumn;
AreaXCustomColumn
#Entity
#Table(name="area_x_custom_column")
#NamedQuery(name="AreaXCustomColumn.findAll", query="SELECT a FROM AreaXCustomColumn a")
public class AreaXCustomColumn implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="area_x_custom_column_uid")
private int areaXCustomColumnUid;
#Column(name="company_id")
private String companyId;
//bi-directional many-to-one association to CustomColumnDataCustomer
#OneToMany(mappedBy="areaXCustomColumn")
private List<CustomColumnDataCustomer> customColumnDataCustomers;
//bi-directional many-to-one association to CustomColumnDefinition
#ManyToOne
#JoinColumn(name="custom_column_definition_uid")
private CustomColumnDefinition customColumnDefinition;
A way to solve this would be with the annotation #EmbeddedId and #JoinColumn.
I needed a similar solution on a project I did recently. I think it'll be easier if I explain it by example:
I have three objects, a Platform, an EventMaster and a Membership.
The Membership is your Customer in this case, it has two PKs, the Platform ID and the EventMaster ID (this is solved by an #EmbeddedID):
#EmbeddedId
private MembershipKey id;
The MembershipKey class simply consists of both PKs of the other class:
#ManyToOne
#JoinColumn(name = "eventmaster_id")
private EventMaster eventMaster;
#ManyToOne
#JoinColumn(name = "mosplatform_id")
private MOSPlatform platform;
The Platform and the EventMasterclass both look the same (this is in the Platformclass):
#OneToMany(mappedBy = "id.platform")
private List<Membership> memberships;
I think that this should help you work out your solution.
EDIT: Code in the question was edited in.

Join Table and Spring Data Repository

This is my sample schema and I have generated jpa entities in eclipse.
I am using spring jpa repositories. I want to know if I need to create repository interface for student course table.
I am having doubt over addStudentCourse method of both student and course entity classes. List studentCourses will be always null for new entity, how can I fill student course table while registering student information in system i.e save method on studentRepository.
Student.java
#Entity
#NamedQuery(name="Student.findAll", query="SELECT s FROM Student s")
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long studentid;
private String studentname;
//bi-directional many-to-one association to StudentCourse
#OneToMany(mappedBy="student")
private List<StudentCourse> studentCourses;
........
public StudentCourse addStudentCourse(StudentCourse studentCourse) {
getStudentCourses().add(studentCourse);
studentCourse.setStudent(this);
return studentCourse;
}
public StudentCourse removeStudentCourse(StudentCourse studentCourse) {
getStudentCourses().remove(studentCourse);
studentCours.setStudent(null);
return studentCourse;
}
Course.java
#Entity
#NamedQuery(name="Course.findAll", query="SELECT c FROM Course c")
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long courseid;
private String coursename;
//bi-directional many-to-one association to StudentCourse
#OneToMany(mappedBy="course")
private List<StudentCourse> studentCourses;
public StudentCourse addStudentCourse(StudentCourse studentCourse) {
getStudentCourses().add(studentCourse);
studentCourse.setCourse(this);
return studentCourse;
}
public StudentCourse removeStudentCourse(StudentCourse studentCourse) {
getStudentCourses().remove(studentCourse);
studentCourse.setCourse(null);
return studentCourse;
}
StudentCourse.java
#Entity
#Table(name="STUDENT_COURSE")
#NamedQuery(name="StudentCourse.findAll", query="SELECT s FROM StudentCourse s")
public class StudentCourse implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private StudentCoursePK id;
private String status;
//bi-directional many-to-one association to Course
#ManyToOne
#JoinColumn(name="COURSEID")
private Course course;
//bi-directional many-to-one association to Student
#ManyToOne
#JoinColumn(name="STUDENTID")
private Student student;
...
}
StudentCoursePK.java
#Embeddable
public class StudentCoursePK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(insertable=false, updatable=false)
private long studentid;
#Column(insertable=false, updatable=false)
private long courseid;
...
}
If I understood your question correctly what you want to do is to be able to save a student from the save method in StudentRepository, and that this inserts/updates the student and also inserts/updates the join table.
Since the Student entity is not the owning side (it's mapped by "student" in StudentCourse), saving a Student will not trigger a save on StudentCourse. To do so you can add a cascade property the list for insert, update... or just for everything:
#OneToMany(mappedBy="student", cascade = CascadeType.ALL)
private List<StudentCourse> studentCourses = new ArrayList<StudentCourse>();
Then you could a method on your #Service class that looks like this:
#Transactional
public void enrollInCourse(Student student, Course course) {
StudentCourse sc = new StudentCourse();
sc.setStudent(student);
sc.setCourse(course);
sc.setStatus("Enrolled");
student.getStudentCourses().add(sc);
studentRepository.save(student);
}
This will also populate the StudentCourse table.
So there's no need for a repository, although if the cascade doesn't work as expected you could create one and save the StudentCourse entity yourself manually.
If this does not work you could try changing your mappings. For n-ary relationships or join tables with extra columns I always define the #ManytoOne relationships inside the #Embeddable class, and in the entity that represents the join table I define getters as #Transient to allow access to the mapped objects which are inside the embedded composite Id.
You can see an example here, and a blog post about this approach here.

JPA reusing code by extending without inheritance

I have two or more tables resembles each other.
PARENT
ID | PK
NAME | VARCHAR
CHILD
ID |PK
NAME | VARCHAR
AGE | INT
It's not #Inheritance situation because they are independent entities and related to each other by #OneToMany or #ManyToOne.
I create entity class for each other.
#Entity
public class Parent {
#Id
private Long id;
private String name;
#ManyToOne(mappedBy = "parent")
private Collection<Child> children;
}
#Entity
public class Child {
#Id
private Long id;
private String name;
private int age;
#OneToMany
private Parent parent;
}
Is there any nice way to share common fields mappings?
// #MappedSuperclass // is this what it is exactly for?
public abstract class Base {
// #Id protected Long id; // ##?
#Column(name = "name", nullable = false)
private String name;
}
#Entity
public class Parent extends Base {
#Id
#TableGenerator(...)
#GeneratedValue(...)
protected Long id;
#ManyToOne(mappedBy = "parent")
private Collection<Child> children;
}
#Entity
public class Child extends Base {
#Id
#TableGenerator(...)
#GeneratedValue(...)
protected Long id;
private int age;
#OneToMany
private Parent parent;
}
Is this OK?
Is it even possible declaring #Id protected Long id; on the Base leaving #TableGenerator and #GeneratedVAlue on extended classes?
Is there any nice way to share common fields mappings?
MappedSuperclass is exactly right tool for that.
Is it even possible declaring #Id protected Long id; on the Base
leaving #TableGenerator and #GeneratedVAlue on extended classes?
No, it is not possible.