Ebean: How to correctly select rows where values? - java

I have a question concerning Ebean. Unfortunately I cannot find any documentation to help me. I would like to select all rows where a value is true and where all members of a OneToMany relationship also have a value which is true.
Here is an example:
#Entity
public class Book extends Model {
#Id
public Long id;
public Boolean isAvailable;
public static Finder<Long, Book> find = new Finder<>(Long.class, Book.class);
}
I would like to complete the findAllByIsAvailable method:
#Entity
public class Category extends Model {
#Id
public Long id;
public Boolean isAvailable;
#OneToMany(mappedBy="category", cascade=CascadeType.ALL)
public List<Book> books;
public static Finder<Long, Category> find = new Finder<>(Long.class, Category.class);
public static List<Category> findAllByIsAvailable() {
// Find all categories where isAvailable == true and books.isAvailable == true
}
}

First you need to add a #ManyToOne relationship to your Book class as :
#Entity
public class Book extends Model {
#Id
public Long id;
public Boolean isAvailable;
#ManyToOne
public Category category;
public static Finder<Long, Book> find = new Finder<>(Long.class, Book.class);
}
Then you will be able to insert in your findAllByIsAvailable() function this
return Category.find.fetch("books").where().eq("isAvailable",true).eq("books.isAvailable",true).findList();
.fetch("books") means that you make a left join of two tables.
You can read a lot about Ebean here http://www.avaje.org/doc/ebean-userguide.pdf
There also are some examples.

Related

How to persist a property of type List<Object> in JPA?

What is the best way to get a List with Objects persisted? I'm using JavaEE 7 with JPA. Object B is also an Entity.
My Model
#Entity
public class ObjectA implements Serializable {
#Id #GeneratedValue
private Long id;
// What should I use here?
private List<ObjectB> objectList;
public ObjectA () {
this.objectList = new ArrayList<ObjectB>();
}
}
My JPA Calls
#Stateless
#JPA
public class ObjectJPA{
#PersistenceContext(unitName = "ObjectProjectPU")
private EntityManager em;
// How can I insert a record in the table for objectList here?
public void insertIntoObjectList(ObjectB objectB) {
em.persist(objectB); // This must be wrong?
}
}
What I prefer is that my database table for objectList would look like this:
id (ObjectA_id) | objectB_id or id | ObjectA_id | objectB_id
Your ObjectA class should look like below.
#Entity
public class ObjectA implements Serializable {
#Id #GeneratedValue
private Long id;
// What should I use here?
private List<ObjectB> objectList;
#OneToMany(mappedBy = "objectB_id")
public List<ObjectB> getObjectList() {
return objectList;
}
public void setObjectList(List<ObjectB> listOfObjetB) {
this.objectList = listOfObjetB;
}
public ObjectA () {
this.objectList = new ArrayList<ObjectB>();
}
}
This will create a relation between your class A and B.
Follow the example given to the below link properly. It will help you to understand the JPA one to many relationship. :)
I figured out that it was pretty easy.
ObjectA Model
#OneToMany
#JoinTable
(
name="OBJECTA_OBJECTB",
joinColumns={ #JoinColumn(name="ID", referencedColumnName="ID") },
inverseJoinColumns={ #JoinColumn(name="OBJECTB_ID", referencedColumnName="ID") }
)
private List<ObjectB> objectList;
JPA CALL
public void insertIntoObjectList(ObjectB object) {
object.setSomeDataInObjectList(list); // just a random list here
em.persist(object);
}
ObjectB Model
#ManyToOne
ObjectA objectA;

Custom bridge table in playframework ebean

I am unsuccessfuly trying to create bridge table that would resolve two #ManyToMany relations. However this table have to contain additional field. For example:
Course: -course_id - pk
Student: -student_id -pk
Bridge: -(course_id, student_id) - pk
-additional_field
My student class looks like this:
#Entity
public class Student extends Model {
#Id
#OneToMany
public List<Bridge> student_id;
}
Course class is basicaly the same.
Bridge table looks like this:
#Entity
public class Bridge extends Model{
#EmbeddedId
public compound_key student_course;
public String additional_field;
#Embeddable
public class compound_key{
#ManyToOne
public Student student_id;
#ManyToOne
public Student course_id;
}
}
Thank you for help.
I have found the following solution. This is a solution without a composite key in Bridge. I added normal #Id field in Bridge class and relations to Student and Course are normal relations.
This solution contains an additional 'id' field in the 'bridge' table in the database.
Here is the code:
Student.java:
#Entity
public class Student extends Model {
#Id
public Integer id;
#OneToMany(mappedBy="student")
public List<Bridge> bridges;
public static Finder<Integer,Student> find = new Finder<Integer,Student>(
Integer.class, Student.class
);
}
Course.java:
#Entity
public class Course extends Model {
#Id
public Integer id;
#OneToMany(mappedBy="course")
public List<Bridge> bridges;
public static Finder<Integer,Course> find = new Finder<Integer,Course>(
Integer.class, Course.class
);
}
Bridge.java:
#Entity
public class Bridge extends Model {
#Id
public Integer id;
#ManyToOne public Student student;
#ManyToOne public Course course;
public String additional_field;
public static Finder<Integer,Bridge> find = new Finder<Integer,Bridge>(
Integer.class, Bridge.class
);
}
EDIT
After many attempts I have found solution with composite key in Bridge class. Classes Student and Course are the same as in previous solution.
Bridge.java changed to following:
#Entity
public class Bridge extends Model {
Bridge() {
bridgeId = new BridgeId();
}
#EmbeddedId
protected BridgeId bridgeId;
#ManyToOne
#JoinColumn(name = "student_id", insertable = false, updatable = false)
private Student student;
#ManyToOne
#JoinColumn(name="course_id", insertable = false, updatable = false)
private Course course;
public String additional_field;
public Student getStudent() {
return student;
}
public void setStudent(Student aStudent) {
student=aStudent;
bridgeId.student_id = aStudent.id;
}
public Course getCourse() {
return course;
}
public void setCourse(Course aCourse){
course=aCourse;
bridgeId.course_id = aCourse.id;
}
}
And there is additional BridgeId.java:
#Embeddable
public class BridgeId implements Serializable
{
public Integer student_id;
public Integer course_id;
public int hashCode() {
return student_id + course_id;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
BridgeId b = (BridgeId)obj;
if(b==null)
return false;
if (b.student_id == student_id && b.course_id == course_id) {
return true;
}
return false;
}
}
What is more important in this code is:
Fields of embedded id are mapped to the same columns as ManyToOne relations.
Value to 'student_id' and 'course_id' columns are inserted from embedded id and not from relations. This is because relations have attributes 'insertable' and 'updatable' set to false.
I had to add getters and setters to 'student' and 'course' fields. In setters I am updating fields of embedded key.
Above solution has several workarounds. But I wasn't ableto find easier and cleaner one.

CrudRepository: find by multiple related entities

I'm having some trouble designing a query in a CrudRepository.
I have two entities, CourseOffering and Department (only relevant code shown):
CourseOffering.java:
public class CourseOffering implements Serializable
{
private Department department;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "DepartmentId", nullable = true)
#JsonProperty
public Department getDepartment()
{
return this.department;
}
public void setDepartment(Department department)
{
this.department = department;
}
}
Department.java:
public class Department implements Serializable
{
private Set<CourseOffering> courses;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
public Set<CourseOffering> getCourses() {
return this.courses;
}
public void setCourses(Set<CourseOffering> courses) {
this.courses = courses;
}
}
and the CrudRepository in question:
CourseOfferingRepository.java:
import java.util.List;
import edu.ucdavis.dss.dw.entities.CourseOffering;
import org.springframework.data.repository.CrudRepository;
public interface CourseOfferingRepository extends CrudRepository<CourseOffering, Long>
{
CourseOffering getOneByTermIdAndNumberAndDepartmentId(long termId, String number,
long departmentId);
List<CourseOffering> findByDepartmentCode(String deptCode);
//List<CourseOffering> findAllByDepartmentCode(String deptCodes);
List<CourseOffering> findByTermCode(String termCode);
}
The three functions in CourseOfferingRepository which are not commented out work as expected. I am trying to get the fourth to work.
What I'd like to do is be able to return all CourseOfferings where the department code is one of many department codes. Note that the CourseOffering table itself only holds a department_id integer which references the ID in the Department table, where the actual deptCode is stored.
How would I go about getting that commented out CrudRepository function to work properly? Or put another way, how does one make the plural version of "List findByDepartmentCode(String deptCode);"?
Thanks in advance for any advice you can offer.
You need to change the commented out code to:
List<CourseOffering> findByDeptCodeIn(Collection<String> deptCodes)
Check out this part of the documentation to see what other keywords are allowed
As geoand pointed out in the comments, the answer is:
List<CourseOffering> findByDepartmentCodeIn(List<String> deptCodes);
Thanks geoand!

Deleting in cascade with native query

I have a Ebean Entity model with 3 entities related with #OneToMany relationships like this:
public class A extends Model {
#Id
#GeneratedValue
public Long id;
public String name;
#OneToMany(cascade = CascadeType.ALL)
public List<B> bList;
...
}
public class B extends Model {
#Id
#GeneratedValue
public Long id;
public String name;
#OneToMany(cascade = CascadeType.ALL)
public List<C> cList;
...
}
public class C extends Model {
#Id
#GeneratedValue
public Long id;
public String name;
...
}
And I want to delete al Bs and Cs of a specific A object. I know that Ebean can take care of the deletion of Cs if I make something like this:
for (B b : a.bList) {
b.delete();
}
but I don't think this is the best solution. I wanted to make something like this:
String sql = "DELETE FROM B WHERE B.a_id="+a.id;
SqlUpdate update = Ebean.createSqlUpdate(sql);
update.execute();
But it's a native SQL and it outputs a 'ConstraintViolationException' because it doesn't have an 'ON DELETE CASCADE'.
What would be the best solution?
com.avaje.ebean.Ebean class has delete method which takes collection as argument:
static int delete(Collection<?> c) // Delete all the beans from a Collection.
So you can use following code:
Ebean.delete(a.bList);
a.bList = new ArrayList<B>();

How to work with interfaces and JPA

I should start out by saying that I am fairly new to Java EE and that I do not have a strong theoretical background in Java yet.
I'm having trouble grasping how to use JPA together with interfaces in Java. To illustrate what I find hard I created a very simple example.
If I have two simple interfaces Person and Pet:
public interface Person
{
public Pet getPet();
public void setPet(Pet pet);
}
public interface Pet
{
public String getName();
}
And an Entity PersonEntity which implements Person as well as a PetEntity which implements Pet:
#Entity
public class PersonEntity implements Person
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private PetEntity pet;
#Override
public void setPet(Pet pet)
{
/* How do i solve this? */
}
}
#Entity
public class PetEntity implements Pet
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
/* Getters and Setters omitted */
}
How do I properly handle the case in the setPet method in which I want to persist the relationships between the two entities above?
The main reason I want to use interfaces is because I want to keep dependencies between modules/layers to the public interfaces. How else do I avoid getting a dependency from e.g. my ManagedBean directly to an Entity?
If someone recommends against using interfaces on entities, then please explain what alternatives methods or patterns there are.
You can use targetEntity property in the relationship annotation.
#Entity
public class PersonEntity implements Person {
private Long id;
private Pet pet;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
#OneToOne(targetEntity = PetEntity.class)
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
}

Categories

Resources