How use projections in spring-data-jpa? - java

I need to get all the information about the ticket in one request, also the name, author, and year of the book. I have implemented this :
I create interface TicketWithBookView
public interface TicketWithBookView {
Date getGiveAway();
Long getReaderId();
Date getTake();
interface Book {
String getAuthor();
String getName();
Integer getYearCreation();
}
}
My entities TicketEntity
#Data
#Entity
#Table(name = "ticket")
#NoArgsConstructor
#AllArgsConstructor
public class TicketEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private Long readerId;
#Column(nullable = false)
private Long bookId;
#Column(nullable = false)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date take;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date giveAway;
}
And second entity BookEntity;
#Entity
#Data
#NoArgsConstructor
#Table(name = "book")
public class BookEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String author;
private Integer yearCreation;
private Integer count;
}
And repository
#Repository
public interface TicketRepository extends CrudRepository<TicketEntity, Long> {
List<TicketWithBookView> findAllByGiveAwayIsNullAndTakeIsNotNull();
}

No way) Projections are used to select data from a query, and not to obtain data from other tables.
You can upload data from another table and create a new model in the service.

probably problem is with AAnd in method name
findAllByGiveAwayIsNullAAndAndTakeIsNotNull
add error message that you get, It would be easier to find problem

Related

Spring REST + JPA + h2: How to instantiate dummy data for a many to many relationship

I'm trying to build a little project-management tool as my first Spring Boot / JPA / H2 / REST Application using lombok annotations for avoiding boilerplate-code. I followed several promising tutorials. But I'm failing at the very end, when I try to intantiate some dummy data to test the database and start the service.
Till now it had two tables: "T_PROJECT" & "T_EMPLOYEE"
But I also want to be able to visualize, in which period an employee works for a specific project. So I need a third table "T_EMPLOYEE_ACTIVITY" with two extra columns: "START_DATE" & END_DATE".
I made an
ER-Diagram that should help to understand how these tables must work together.
I found already this one here:
JPA 2.0 many-to-many with extra column
... and tried to build it the same way:
The Project entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_PROJECT")
public class Project implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROJECT_ID")
private Long id;
private String name;
#Column(name = "START_DATE")
private String startDate;
#Column(name = "END_DATE")
private String endDate;
private Status status;
#OneToMany(mappedBy = "project")
#Column(name = "EMPLOYEE_ACTIVITIES")
private Set<EmployeeActivity> employeeActivities;
}
The Employee entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_EMPLOYEE")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "EMPLOYEE_ID")
private Long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
private String role;
#Column(name = "HOURS_PER_WEEK")
private BigDecimal hoursPerWeek;
#OneToMany(mappedBy = "employee")
#Column(name = "EMPLOYEE_ACTIVITIES")
private Set<EmployeeActivity> employeeActivities;
}
EmployeeActivity entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_EMPLOYEE_ACTIVITY")
public class EmployeeActivity implements Serializable {
#Id
#Column(name = "EMPLOYEE_ACTIVITY_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "PROJECT_ID")
private Project project;
#ManyToOne
#JoinColumn(name = "EMPLOYEE_ID")
private Employee employee;
#Column(name = "START_DATE")
private String startDate;
#Column(name = "END_DATE")
private String endDate;
}
In the Application.java (with main & run method), I tried to intantiate it like this and failed:
#Override
public void run(String... args) throws Exception {
Project project = new Project(null, "BERLIN_AIRPORT", "2006-05-01", "2020-10-31", Status.COMPLETED, Set.of( ??? ));
projectRepository.save(project);
Employee employee = new Employee(null, "Jim", "Beam", "Architect", BigDecimal.valueOf(40d), Set.of( ??? ));
employeeRepository.save(employee);
EmployeeActivity employeeActivity = new EmployeeActivity();
employeeActivity.setProject(project);
employeeActivity.setEmployee(employee);
employeeActivity.setStartDate("2006.05.01");
employeeActivity.setEndDate("2010.12.12");
employeeActivityRepository.save(employeeActivity);
}
So both - Project and Employee - have an attribute "employeeActivities", that needs some value, when I make a new Object.
But at this point, there is no EmployeeActivity-Object that i could use.
How do I manage this?
Thanks a lot & have nice day!
NicerDicer
I meanwhile found the solution. The problem was, that I tried to use the AllArgsConstructor that has been generated via lombok.
The AllArgsConstructor expects of course all attributes that I declared in the entitties.
The solution is to use setters (in my case auto-generated by the lombok #Data annotation) and to not set the id and employeeActivities from project & employee.
(Alternatively you can of course write your own constructor.)
#Override
public void run(String... args) throws Exception {
Project project_1 = new Project();
project_1.setName("BERLIN_AIRPORT");
project_1.setStartDate("2006-05-01");
project_1.setEndDate("2020-10-31");
project_1.setStatus(Status.COMPLETED);
projectRepository.save(project_1);
Employee employee_1 = new Employee();
employee_1.setFirstName("Jim");
employee_1.setLastName("Beam");
employee_1.setRole("Architect");
employee_1.setHoursPerWeek(BigDecimal.valueOf(40d));
employeeRepository.save(employee_1);
EmployeeActivity employeeActivity = new EmployeeActivity();
employeeActivity.setProject(project_1);
employeeActivity.setEmployee(employee_1);
employeeActivity.setStartDate("2019-05-01");
employeeActivity.setEndDate("2022-12-12");
employeeActivityRepository.save(employeeActivity);
}

One to Many Relation mapping using org.mapstruct framework

How can I map the one to many relationship using org.mapstruct framework?
DTO classes:
#Data
public class ScheduledJobDTO {
private String jobName;
private String jobGroup;
private String jobClass;
private String cronExpression;
private Boolean cronJob;
private Long repeatTime;
private Integer repeatCount;
private Set<ScheduledJobParamsDTO> paramtersDTOs;
}
#Data
#EqualsAndHashCode
public class ScheduledJobParamsDTO {
String name;
String value;
}
Domain Classes -
#Data
#Entity
#Table(name = "scheduled_job")
public class ScheduledJob {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "job_name")
private String jobName;
#Column(name = "job_group")
private String jobGroup;
#Column(name = "job_class")
private String jobClass;
#Column(name = "cron_expression")
private String cronExpression;
#Column(name = "is_cron_job")
private Boolean cronJob;
#Column(name = "repeat_time")
private Long repeatTime;
#Column(name = "repeat_count")
private Integer repeatCount;
#Column(name = "trigger_start_date")
private LocalDate triggerStartDate;
#Column(name = "trigger_end_date")
private LocalDate triggerEndDate;
#Column(name = "created_at")
private LocalDate createdAt;
#Column(name = "modified_at")
private LocalDate modifiedAt;
#Column(name = "is_active")
private Boolean active;
#OneToMany(mappedBy = "scheduledJob")
private Set<ScheduledJobParams> parameters;
}
#Data
#Entity
#Table(name = "scheduled_job_params")
#EqualsAndHashCode
public class ScheduledJobParams {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "scheduled_job_id", nullable = false)
ScheduledJob scheduledJob;
String name;
String value;
}
Mapper Class -
#Mapping(source = ".", target = ".")
#Mapping(source = "paramtersDTOs", target = "parameters")
ScheduledJob mapToDomain(ScheduledJobDTO scheduledJobDTO);
Now, the above mapper is mapping the ScheduledJob & ScheduledJobParams, but the ScheduledJobParams has reference of ScheduledJob.
How can I map the reference ScheduledJob to ScheduledJobParams?
You can achieve that through #AfterMapping with #MappedTarget. This is described in the reference documentation: 12.2. Mapping customization with before-mapping and after-mapping methods.
// Java 8+ otherwise you need to use an abstract class and a for-loop instead
#Mapper(componentModel = "spring")
public interface ScheduledJobMapper {
#Mapping(target = "parameters", source = "paramtersDTOs")
ScheduledJob mapToDomain(ScheduledJobDTO dto);
#AfterMapping
default void after(#MappingTarget ScheduledJob domain, ScheduledJobDTO dto) {
domain.getParameters().forEach(scheduledJobParams -> {
scheduledJobParams.setScheduledJob(domain);
});
}
}
However, I am sure you don't need to fill the bidirectional relationship when you map back from the DTO into the entity (this is what I understand as you refer to "domain"). Note printing out or serializing such object i.e. into JSON or XML throws java.lang.StackOverflowError if not properly handled.

Loading DTO with collection

#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)

How can you make a created_at column generate the creation date-time automatically like an ID automatically gets created?

I currently have an Entity as below:
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productId;
private String productImage;
private String productTitle;
private String productDescription;
private Integer productPrice;
private Date createdAt;
private Date updatedAt;
Upon creation of this object, the value of createdAt and updatedAt shows null in the database and was wondering how I can implement code so that createdAt and updateAt automatically gets inserted?
My post method is as below:
#PostMapping("/products")
public ProductResponse createProduct(#Validated #RequestBody ProductForm productForm) {
Product product = productForm.asProduct();
Product createdProduct = productRepository.save(product);
return new ProductResponse(createdProduct, "Product created");
}
JPA
There isn't anything as convenient as annotating the Timestamp field directly but you could use the #PrePersist, #PreUpdate annotations and with little effort achieve the same results.
Hibernate
#CreationTimestamp - Documentation
#UpdateTimestamp - Documentation
Spring Data JPA
#CreatedDate - Documentation
#LastModifiedDate - Documentation
Extend the following abstract class in your entity:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class DateAudit implements Serializable {
#CreatedDate
#Column(name = "created_at", nullable = false, updatable = false)
private Date createdAt;
#LastModifiedDate
#Column(name = "updated_at")
private LocalDateTime updatedAt;
}
Don't forget to enable JPA Auditing feature using #EnableJpaAuditing
Read this: https://docs.spring.io/spring-data/jpa/docs/1.7.0.DATAJPA-580-SNAPSHOT/reference/html/auditing.html
With the mix of #dimitrisli and #buddha answers, something pretty clean is
#Data
#MappedSuperclass
public abstract class BaseEntity {
#Column(updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
#UpdateTimestamp
private LocalDateTime updatedAt;
}
And now you all your entity can extend that class like so
#Data
#Entity
#EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
#Id
#GeneratedValue
public UUID id;
public String userName;
public String email;
public String firstName;
public String lastName;
}
Note that you might not need #Data & #EqualsAndHashCode annotations from lombok as it generate getter/setter
You can create a BaseEntity. Each entity extends the BaseEntity. In the Base entity ,it will set the time automatically
#Data
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity implements Serializable {
#Id
#Column(name = "Id")
private String id;
#Column(name = "deleted", columnDefinition = "Bit(1) default false")
private boolean deleted = false;
#Column(name = "DataChange_CreatedBy", nullable = false)
private String dataChangeCreatedBy;
#Column(name = "DataChange_CreatedTime", nullable = false)
private Date dataChangeCreatedTime;
#Column(name = "DataChange_LastModifiedBy")
private String dataChangeLastModifiedBy;
#Column(name = "DataChange_LastTime")
private Date dataChangeLastModifiedTime;
#PrePersist
protected void prePersist() {
if (this.dataChangeCreatedTime == null) dataChangeCreatedTime = new Date();
if (this.dataChangeLastModifiedTime == null) dataChangeLastModifiedTime = new Date();
}
#PreUpdate
protected void preUpdate() {
this.dataChangeLastModifiedTime = new Date();
}
#PreRemove
protected void preRemove() {
this.dataChangeLastModifiedTime = new Date();
}
}

How can I convert this 3 JOIN query into a Spring Data JPA named query method?

I am not so into Spring Data JPA and I have the following problem trying to implement a named query (the query defined by the method name).
I have these 3 entity classes:
#Entity
#Table(name = "room_tipology")
public class RoomTipology implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "tipology_name")
private String name;
#Column(name = "tipology_description")
private String description;
#Column(name = "time_stamp")
private Date timeStamp;
#OneToMany(mappedBy = "roomTipology")
private List<Room> rooms;
#OneToOne(mappedBy = "roomTipology")
private RoomRate roomRate;
// GETTER AND SETTER METHODS
}
That represents a tipology of room and that contains this field
#OneToMany(mappedBy = "roomTipology")
private List<Room> rooms;
So it contains the list of room associated to a specific room tipology, so I have this Room entity class:
#Entity
#Table(name = "room")
public class Room implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "id_accomodation_fk", nullable = false)
private Accomodation accomodation;
#ManyToOne
#JoinColumn(name = "id_room_tipology_fk", nullable = false)
private RoomTipology roomTipology;
#Column(name = "room_number")
private String number;
#Column(name = "room_name")
private String name;
#Column(name = "room_description")
#Type(type="text")
private String description;
#Column(name = "max_people")
private Integer maxPeople;
#Column(name = "is_enabled")
private Boolean isEnabled;
// GETTER AND SETTER METHODS
}
Representing a room of an accomodation, it contains this annoted field:
#ManyToOne
#JoinColumn(name = "id_accomodation_fk", nullable = false)
private Accomodation accomodation;
And finally the Accomodation entity class:
#Entity
#Table(name = "accomodation")
public class Accomodation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy = "accomodation")
private List<Room> rooms;
#Column(name = "accomodation_name")
private String name;
#Column(name = "description")
#Type(type="text")
private String description;
// GETTER AND SETTER METHODS
}
Ok, so now I have this Spring Data JPA repository class for RoomTipology:
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface RoomTipologyDAO extends JpaRepository<RoomTipology, Long> {
}
Here I want to define a named query method that return to me the list of all the RoomTipology object related to a specific accomodation, I have done it using SQL and it works fine:
SELECT *
FROM room_tipology as rt
JOIN room r
ON rt.id = r.id_room_tipology_fk
JOIN accomodation a
ON r.id_accomodation_fk = a.id
WHERE a.id = 7
But now I want to translate it in a named query method (or at least using HQL)
How can I do it?
Please Try:
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface RoomTipologyDAO extends JpaRepository<RoomTipology, Long> {
List<RoomTipology> findByRooms_Accomodation(Accomodation accomodation);
}
The query builder mechanism built into Spring Data repository infrastructure is useful for building constraining queries over entities of the repository. The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it
At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties.
Doc:Here

Categories