I used to know how to do this but for the life of me can't even remember what to google for! I have a bunch of POJOs with a handful of simple fields (primitives, Strings, etc) each, and I want to just add them all into a parent POJO and store the whole lot together as a single row in a single table (without having to duplicate and copy every field from the child POJOs into the parent POJO). I believe there is an annotation, or perhaps a Hibernate converter, that I can use to do this very easily?
As an example, say I have:
class A {
Integer foo;
String bar;
}
class B {
Integer lol;
String rofl;
}
I want a parent object like:
#Entity
class P {
A a;
B b;
}
Which will have a database schema of foo,bar,lol,rofl
Any help much appreciated!
I think you have 2 Classes, having some common attributes. So you want to create another common class that should be used with those classes but will not be mapped to any specific table in database(I mean this common class will have existence/will be handled just on application level). Like Consider 2 class Student & Teacher, having some common attributes like name, gender etc. So now can push those common attributes in a common class like for example Human. So if that the case, than do something like this:
#Embeddable
public class Human{
private String name;
private String gender;
//Constructors, getters & setters
}
Now Create a specific class like Student
public class Student{
#Embedded
private Human human;
private String rollNo;
//....
//Constructors, getters & setters
}
and so for Teacher class.
Now you can save these entries like:
Human human = new Human();
human.setName("your-name");
//....
Student student = new Student();
student.setHuman(human);
student.setRollNo("343");
//....
and save it like
studentRepository.save(student);
So in database, just one record will be saved for Student(name, gender, rollNo ...). Because here in this case, Human is just the combination of parameters of Student and is not an entity.
Related
I have a Student class.
#Entity
public class Student {
private long id;
private String name;
private String department;
private String subDepartment;
private int marks;
//getters, setters and constructors.
}
I want to be able to get min, max and average marks grouped by department and then subDepartment.
My repository:
public class StudentRepository extends JpaRepository<Student,Long> {
#Query("Select s.department, s.subDepartment , min(marks), max(marks), avg(marks) from Student as s groupby s.department, s.subDepartment")
List<Object[]> getStatsByDepartmentAndSubDepartment()
}
Of course, this works but I want to avoid using List<Object[]> and instead get something like:
Map<String, Map<String,Stats>> class
Where first key is Department, second key is subDepartment and Stats class encapsulates min,max,average
Is there a way to do this with Spring JPA. ? I checked and nested classes are not possible.
How about Hibernate or any other solution? I am a newbie when it comes to Hibernate.
I am using Spring boot and can add any dependency needed. Any database is ok, even H2 for a start.
You cannot return a map.
You can convert the rows using a class DTO.
Class DTO with constructor
You can also create a class DTO:
class StudentDTO {
public StudentDTO(String dep, String subDep, Integer min, Integer max, Double avg ) {
this.stats = new Stats(min, max, avg);
...
}
}
And then call it in the select clause:
select new StudentDTO(s.department, s.subDepartment, avg(s.marks), ...) from ...
This will return a List<StudentDTO>.
As long as the constructor matches the select clause, it will work fine.
Interface DTO
You can also define DTO using interfaces, but I don't think it works if you have nested classes.
This will work though:
interface StudentViev {
String getDepartment();
String getSubdepartment();
Integer getMinMark();
Integer getMaxMark();
Double getAvgMark();
}
and now you can use it as type for the list:
#Query("Select s.department as department, s.subDepartment as subDepartment, min(marks) as minMark, max(marks) as maxMark, avg(marks) as avgMark from Student as s groupby s.department, s.subDepartment")
List<StudentView> getStatsByDepartmentAndSubDepartment()
Note that I've used aliases in the select cluase so that the column names returned match the getters in the interface
Imagine you have a journal of student evaluations. Each student have some marks of each subject in a journal in some day. Is there a better way to add mark of student by specific date?
I tried to store this data in HashMap<Student, HashMap<Subject, ArrayList<Integer>>> (my previous question) but I think this way is bulky and is not efficient enough.
class School {
private List<Classes> classes;
...
}
/**
* Classes contains data of school classes (students, subjects etc)
*/
class Classes {
private List<Students> students;
private List<Subjects> subjects;
private List<Teachers> teachers;
...
}
/**
* Class Mark is a collection of student marks
* Contains Mark, Subject and Date values
*/
class Mark {
private Date date;
...
}
Any ideas to store this one more efficient with create/read/update/delete actions? I'll be glad to get tips how do I organize these classes.
The simplest solution to your problem is to have Mark class with the following fields:
public class Mark {
private Integer value;
private Date date;
private Student student;
private Subject subject;
//getters & setters
}
This will allow to get all the marks of a concrete student from any Collection<Mark> marks using filtering methods (google guave libs provide such an API, with Java 8 it's even easier to do as it is now in java.util.stream).
Nevertheless, if you are willing to have your data structure with marks already sorted on a concrete student, I would suggest to implement a backward relation between Student and Mark:
public class Student {
private Collection<Mark> marks;
//getters & setters
}
Now you operate on Collection<Student> students and may get the marks on a concrete student from the collection.
Finally, if you would like to operate on a data structure with marks already sorted by Student and Subject you may use an additional abstraction:
public class StudentPerformance {
private Student student;
private Map<Subject, List<Mark>> marks; //actually it can be implemented
//with a class as well but let's leave it as it is
//for the sake of simplicity of the example
//setters & getters
}
And then store it as a collection: Collection<StudentPerformance> performances
The pros for such a solution:
Less verbosity in your client code
It is more descriptive
You use one of the main features of Java - strong typing. It means that it is guaranteed in compile time that your client code would not accept anything which is not of type StudentPerformance. And it really helps to avoid bugs in large projects.
Finally, the concrete solution is always dependent on the needs of your application. There is no silver bullet.
I don't think that use a complex key in a HashMap is a good idea.
I would do something like this:
class Evaluation {
private Subject Subject;
private Date date;
private List<Mark> marks;
//getters & setters
}
class Mark {
Student student;
int score;
//getters & setters
}
So now in Classes you will have:
class Classes {
private List<Students> students;
private List<Subjects> subjects;
private List<Teachers> teachers;
private List<Evaluation> evaluations;
...
}
The difference between #Entity and #Embeddable annotation when each one is added before class declaration?
the first create class as an entity, second insert column from another table?
the first create class as an table, while second is embedded in another class?
the first sets standard as a class, second define table type
the first create table for that class, second embed something into different class
the first define table property, second create union of two tables
#Entity annotation over a class defines that, it has a distinct separate existence. Thus we can run DB queries, without being dependent on any other class. #Embeddable annotation over a class defines that, it does not have independent existence. Thus we cannot run DB queries, without depending on other class. Here is an example to understand it better:
#Entity
User
-- long id
-- String name
-- String email
#Embedded
-- UserDetails userDetail
#Embeddable
UserDetails
-- Date dateOfBirth
-- String sex
-- String address
-- String maritalStatus
Here you can see without having a User, UserDetails is useless.
Generally, in OOP, we first design the classes and then we design database entities. For some classes (like UserDetails class in the above example), we do not want to have separate tables in DB, where their independent existence is meaningless. In those cases, we mark the class as embeddable.
Typically, embeddable classes share the same table as the Entity in which they are embedded
Entities have an identity and can be queried for. Embeddables have no identity of their own and can only be queried for using the owning entities.
If you open an entity class, you will always find the #Id annotation - it is mandatory. If you open an embeddable class, you will never find an #Id annotation - it is forbidden.
EDIT: It is not entirely correct that embeddables can only be stored as a part of the parent, i.e. in the same table. This is only true for one-to-one relationships. You can have Collections and Maps of embeddable objects in the parent entity and they will be mapped to own collection tables.
An entity class is an ordinary user defined Java class whose instances can be stored in the database.
#Entity
#Table(name="dog")
public class Dog{
#Id
#Column(name = "id")
private int id;
#Embedded
private Animal animal;
public Dog(int id,Animal animal){
this.id=id;
this.animal=animal;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
Embeddable classes are user defined persistable classes that function as value types. As with other non entity types, instances of an embeddable class can only be stored in the database as embedded objects, i.e. as part of a containing entity object.
#Embeddable
public class Animal {
#Column(name = "name")
private String name;
#Column(name = "location")
private String location;
public Animal(){
}
public Animal(String name,String location){
this.name=name;
this.location=location;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
It is an old topic but I would like to add my answer, which is more from theoretical point of view. In DDD (domain driven design) we usually have Entity and Value Objects. The first ones are identifiable only by a an identity that they have. The second ones are not defined by an identity, which means that if all the components that make that particular objects are the same, than the 2 value objects are the same.
The analogy is that in this case, if we were to apply DDD, the Entity is the class annotated with #Entity and the Value Object is the one with #Embeddable. A demonstration of this is the fact that the embeddable object is added as addditional information to an existing record, which already has its own identity defined externally to the embedded object.
Well #Entity signifies that the entity object has significance all by itself it doesn't require any further association with any other object. Where as #Embeddable object doesn't carry any significance all by itself, it needs association with some other object.
Lets take an example of say i have a Employee Object and it has a collection of Address Object as its member variable. Now when when speak of any address we need to tell whose address it is, which employees address it is. If we just talk about the address it doesn't make any sense. Hope this gives you the difference between the two.
A class exists that I have no control over:
public class MyPerson {
private String name;
private int elevation;
// getters and setters
}
I want to persist this person into Mongo, but I cannot alter this class definition with #Entity and other annotations.
I'd like the document in Mongo to look something akin to:
{ name : "You", elevation : 65 }
What's the best way to approach this? Converter? Extended class? Containing class?
The easiest way would be to just embed it in one you control, and can put the #Id on.
Must work:
morhpia.map(MyPerson.class);
Datastore ds = morhpia.createDatastore(mongo, "my_database");
MyPerson pe = new MyPerson();
pe.setName("cmonkey");
ds.save(pe);
BUT
You must add #Id field in the POJO. Its is required by Morphia.
I am trying to model such situation - there is a cash transfer (I mean a car that carries money), that has required amounts of each currency, and also an actual amount for each currency. And it seems to me pointless to create two separate classes, one for required amount and another for actual amount. So the implementation would look like this:
#Entity
public class CashTransferCurrencyAmount {
// id, version and so on
#Column(length = 3)
private String currencyCode;
#Basic
private BigDecimal amount;
#ManyToOne
private CashTransfer cashTransfer;
}
#Entity
public class CashTransfer {
// id, version and so on
#OneToMany(mappedBy="cashTransfer")
private Set<CashTransferCurrencyAmount> requiredCurrencyAmountSet = new HashSet<CashTransferAmountCurrency>();
#OneToMany(mappedBy="cashTransfer")
private Set<CashTransferCurrencyAmount> actualCurrencyAmountSet = new HashSet<CashTransferAmountCurrency>();
}
But how is a CashTransferCurrencyAmount instance to know to which collection it belongs? I have two ideas:
1 - add a discriminator field to CashTransferCurrencyAmount:
public enum RequestType {
ACTUAL,
REQUIRED
}
#Basic
#Enumerated(EnumType.STRING)
private RequestType requestType;
and add #WHERE annotations to collections in CashTransfer. This is preferable for me.
2 - create two join tables. one for mapping requested amounts and one for mapping actual amounts. I dislike this one as I don't want too many tables in my DB.
Are there any other ways to achieve this? I this approach correct?
And please don't tell me to put both requested and actual amounts in one entity. The real case is more complicated, each CashTransferCurrencyAmount has it's own collections so it can't be solved that way.
EDIT
As for requests for complete story - there used to be two values in CashTransferCurrencyAmount - required (I think it should be 'requested') and actual, but now each amount has it's own collection - how this amount is split into denominations. So I need a collection of amounts, each one having a collection of denominations. The type of CurrencyAmount and CurencyDenomination seems to be the same for requested ones and for actual ones.
Since you want CashTransferCurrencyAmount instance to know which collection it belongs to, I assume you want to have some logic based on that. The way I would model your situation would be using inheritance.
You're saying "it seems to me pointless to create two separate classes", I would however try to convince you that you should. You could use a "Single Table" inheritance type, so that you don't introduce additional tables in your DB, which is what you're trying to accomplish.
My shot would look something like:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "request_type", discriminatorType = DiscriminatorType.STRING)
public abstract class CashTransferCurrencyAmount {
// id, version and so on
#Column(length = 3)
private String currencyCode;
#Basic
private BigDecimal amount;
#ManyToOne
private CashTransfer cashTransfer;
}
#Entity
#DiscriminatorValue("REQUIRED")
public class CashTransferCurrencyAmountRequired extends CashTransferCurrencyAmount {
// required anount specific stuff here
}
#Entity
#DiscriminatorValue("ACTUAL")
public class CashTransferCurrencyAmountActual extends CashTransferCurrencyAmount {
// actual anount specific stuff here
}
#Entity
public class CashTransfer {
// id, version and so on
#OneToMany(mappedBy="cashTransfer")
private Set requiredCurrencyAmountSet = new HashSet();
//Stackoverflow deleting my generic sets! But it's exactly the same as in your code...
#OneToMany(mappedBy="cashTransfer")
private Set actualCurrencyAmountSet = new HashSet();
}