Suppose I have following class
#Entity
public class Customer extends Model {
#Id
public int id;
public String email;
#ManyToOne
public List<Order> orders;
public HashMap<String, Object> additionalData;
public static Finder<String, Customer> find = new Finder<String, Customer>(String.class, Customer.class);
public static List<Customer> getCustomersWithOpenOrders(){
return find
.fetch("orders")
// with "order.state = 'open'" count > 0
// add total sum of all orders to 'additionalData' collection
.findList();
}
}
How to store sum of all orders in additionalData collection?
Maybe you should take a look at the Formula annotation.
But for using this, you have to create a new property in order to store this value, then you can put this value in your map:
#Entity
public class Customer extends Model {
#Id
public Integer id;
public String email;
#ManyToOne
public List<Order> orders;
#Transient
#Formula(...) // write the query to compute the sum
public Integer totalOrders;
public HashMap<String, Object> additionalData;
public static Finder<Integer, Customer> find = new Finder<Integer, Customer>(Integer.class, Customer.class);
public static List<Customer> getCustomersWithOpenOrders(){
...// call the finder
additionalData.put("sum", totalOrders);
...
}
...
}
Related
Based on my research we have two way for getting related data from two or more tables.
For example if we have 2 tables like below:
#Entity
public class User {
#PrimaryKey public long userId;
public String name;
public int age;
}
#Entity
public class Library {
#PrimaryKey public long libraryId;
public long userOwnerId;
}
If we want to load all data we have two options:
1. #Embedded and #Relation
By adding this class:
public class UserAndLibrary {
#Embedded public User user;
#Relation(
parentColumn = "userId",
entityColumn = "userOwnerId"
)
public Library library;
}
And add DAO method:
#Transaction
#Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();
More information in Android Documentation
2. #DatabaseView
#DatabaseView("SELECT user.id, user.name, user.age, " +
"library.libraryId FROM user " +
"INNER JOIN library ON user.userId = library.libraryId)
public class UserAndLibrary {
public long userId;
public String name;
public int age;
public long libraryId;
}
and a associating
#Database(entities = {User.class, Library.class},
views = {UserAndLibrary.class},
version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
What is the difference between two options?
I want to find a Type document by the code of job and by the list of code of category, i tried the below query but it didn't work
#Document
public class Type {
#Id
private String id;
#DBRef
private Job job;
#DBRef
private List<Category> categories;
}
public class Job {
#Id
private String id;
private String code;
}
public class Category {
#Id
private String id;
private String code;
}
public interface TypeRepository extends MongoRepository<Type, String> {
#Query("{ 'job.code': ?0, 'category.code': { $in: ?1 }}")
Type findByJobAndCategoriesCode(String codeJob, List<String> codeCategories);
}
try using this one
public interface TypeRepository extends MongoRepository<Type, String> {
Type findOneByJobCodeAndCategoriesCodeIn(String codeJob, List<String> codeCategories);
}
I have MyEntity class:
#Entity
#Table("entities)
public class MyEntity {
#ID
private String name;
#Column(name="age")
private int age;
#Column(name="weight")
private int weight;
...getters and setters..
}
In #RestController there are 2 #GetMapping methods.
The first:
#GetMapping
public MyEntity get(){
...
return myEntity;
}
The second:
#GetMapping("url")
public List<MyEntity> getAll(){
...
return entities;
}
It's needed to provide:
1. #GetMapping returns entity as it's described in MyEntity class.
2. #GetMapping("url") returns entities like one of its fields is with #JsonIgnore.
UPDATE:
When I return myEntity, client will get, for example:
{
"name":"Alex",
"age":30,
"weight":70
}
I want in the same time using the same ENTITY have an opportunity depending on the URL send to client:
1.
{
"name":"Alex",
"age":30,
"weight":70
}
2.
{
"name":"Alex",
"age":30
}
You could also use JsonView Annotation which makes it a bit cleaner.
Define views
public class View {
static class Public { }
static class ExtendedPublic extends Public { }
static class Private extends ExtendedPublic { }
}
Entity
#Entity
#Table("entities)
public class MyEntity {
#ID
private String name;
#Column(name="age")
private int age;
#JsonView(View.Private.class)
#Column(name="weight")
private int weight;
...getters and setters..
}
And in your Rest Controller
#JsonView(View.Private.class)
#GetMapping
public MyEntity get(){
...
return myEntity;
}
#JsonView(View.Public.class)
#GetMapping("url")
public List<MyEntity> getAll(){
...
return entities;
}
Already explained here:
https://stackoverflow.com/a/49207551/3005093
You could create two DTO classes, convert your entity to the appropriate DTO class and return it.
public class MyEntity {
private String name;
private int age;
private int weight;
public PersonDetailedDTO toPersonDetailedDTO() {
PersonDetailedDTO person = PersonDetailedDTO();
//...
return person;
}
public PersonDTO toPersonDTO() {
PersonDTO person = PersonDTO();
//...
return person;
}
}
public class PersonDetailedDTO {
private String name;
private int age;
private int weight;
}
public class PersonDTO {
private String name;
private int age;
}
#GetMapping
public PersonDTO get() {
//...
return personService.getPerson().toPersonDTO();
}
#GetMapping("/my_url")
public PersonDetailedDTO get() {
//...
return personService.getPerson().toPersonDetailedDTO();
}
EDIT:
Instead of returning an Entity object, you could serialize it as a Map, where the map keys represent the attribute names. So you can add the values to your map based on the include parameter.
#ResponseBody
public Map<String, Object> getUser(#PathVariable("name") String name, String include) {
User user = service.loadUser(name);
// check the `include` parameter and create a map containing only the required attributes
Map<String, Object> userMap = service.convertUserToMap(user, include);
return userMap;
}
As an example, if you have a Map like this and want
All Details
userMap.put("name", user.getName());
userMap.put("age", user.getAge());
userMap.put("weight", user.getWeight());
Now if You do not want to display weight then you can put only two
parameters
userMap.put("name", user.getName());
userMap.put("age", user.getAge());
Useful Reference 1 2 3
That is the JPA entity MyEntity.
Entity
class MyEntity{
private Integer id;
private Date date;
private Double montant;
#ManyToOne(fetch = FetchType.LAZY)
private User creator;
}
class User {
private Integer id;
private String name;
private String image;
private Integer age;
private String anotherField;
}
I would like to retrieve a list of MyEntity with some attributes of its creator (just the id, the name and its image).
So I created a Projection interface.
interface Projection{
public Integer getId();
public Date getDate();
public Double getMontant();
public User getCreator();
interface User {
public Integer getId();
public String getName();
public String getImage();
}
}
here JPA repository implementation :
public interface CommandeRepository extends JpaRepository<EbCommande, Integer> {
<T> Collection<T> findById(Integer id, Class<T> type);
<T> Collection<T> findByCreator(User client, Class<T> type);
}
The first query works as I hope.
On the other hand with the second, when I loop on the list of MyEntity returned, each access to the User attribute triggers a request to the database fetching all the attributes of the User.
I do not understand how JPA projections work anymore.
Help please!
I am creating an application that uses a many to many relationship between employees and shifts. However, I am having difficulties in understanding how I can assign/connect an employee to a shift.
#Data
#Entity
public class Employee {
private #Id #GeneratedValue long employeeID;
private String Name;
#OneToMany(cascade = CascadeType.ALL)
private Set<Shift> shifts;
private Employee() {
}
public Employee(long employeeID, String Name) {
this.employeeID = employeeID;
this.Name = Name;
}
public Employee(long employeeID, String Name, Set<Shift> shifts) {
this.employeeID = employeeID;
this.Name = Name;
this.shifts = shifts;
}
public void setShift(Set<Shift> shifts) {
this.shifts = (Set<Shift>) shifts;
}
}
#Data
#Entity
public class Shift {
private #Id #GeneratedValue long Id;
private String shifts;
private Set<Employee> employee;
private Shift() {
}
public Shift(String shifts) {
this.shifts = shifts;
}
public Shift(String shiftPeriod,Set<Employee> employee ) {
this.shifts = shifts;
this.employee=employee;
}
public void setEmployee(Set<Employee> employee) {
this.employee = employee;
}
}
#Component
public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository;
#Autowired
public DatabaseLoader(EmployeeRepository repository) {
this.repository = repository;
}
#Override
public void run(String... strings) throws Exception {
Shift shift = new Shift("Friday Morning");
Employee employee = new Employee(0001, "Adam Smith");
employee.setShift(shift);
this.repository.save(employee);
}
}
public interface ShiftRepository extends CrudRepository<Shift, Long>
public interface EmployeeRepository extends CrudRepository<Employee, Long>
Entities added into employees and shifts are saved but is there a way I can assign a shift to an employee in the DatabaseLoader class, as I've been stuck on finding a solution to this.
I know that I haven't included a method which attempts to connect employee and shifts but I don't how to approach this problem.
Thanks in advance
**EDIT: The new problem I have now is that I get the following message when trying to deploy in spring:
Unable to build Hibernate SessionFactory: Could not determine type for: java.util.Set, at table: shift, for columns: [org.hibernate.mapping.Column(employee)]
In my opinion:
A shift is an independent entity. It does not depend on any employees.
So Shift must not have a reference to Employee.
On the other hand an employee depend on several shifts, so Employee must have unidirectional #OneToMany association with Shift.
#OneToMany
private List<Shift> shifts;
And don't use cascading here because Employee and Shift are independent entities.
UPDATE
To "add a shift to an employee" we can use for example an Employee setter:
#Entity
public class Employee {
//...
private String name;
//...
#OneToMany
private List<Shift> shifts;
//...
public Employee(String name) {
this.name = name;
}
//...
public setShifts(Shift... shifts) {
this.shifts = Arrays.asList(shifts);
}
//...
}
then
Shift monday = shiftRepository.save(new Shift("Monday"));
Shift friday = shiftRepository.save(new Shift("Friday"));
Employee adamSmith = new Employee("Adam Smith");
adamSmith.setShifts(monday, friday);
employeeRepository.save(adamSmith);
It seems that you are using Hibernate under the hood, then all you have to do is to set the objects you want to save properly, like this:
#Component
public class DatabaseLoader implements CommandLineRunner {
private final EmployeeRepository repository;
private final ShiftRepository shiftRepository;
#Autowired
public DatabaseLoader(EmployeeRepository repository, ShiftRepository shiftRepository) {
this.repository = repository;
this.shiftRepository=shiftRepository;
}
#Override
public void run(String... strings) throws Exception {
Shift shift = new Shift("Friday Morning");
Employee employee = new Employee(0001, "Adam Smith");
employee.setShift(shift)
this.repository.save(employee);
}
The only difference is that I attached between the two objects and only then I try to save them. It's worth to mention that it's important to use Cascade.All or Cascade.persiste in order that hibernate will do the inserts on both entities.