spring data jpa native query - mysql

I am working on a mini banking app. I want to query the database in which all the transactions made so far are stored with three query parameters which are, user account,startDate and endDate. the database has a column for transactionDate which is of type Date. I want a situation in which if the user provide something like 2022-10-21 as start date and 2022-08-02 as the end date, and then provide his account number. The query should return a list of all the transactions made by that particular user using the user's account number by querying the transactionDate column to get the date for each transactions. That's a transaction between 2022-10-21 to 2022-08-02 for that user.
This is what I have tried so far but still getting this error
Caused by: org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.List com.elijah.onlinebankingapp.repository.transaction.TransactionTypeRepository.findByBankAccountAndTransactionDate(com.elijah.onlinebankingapp.model.account.BankAccount,java.util.Date,java.util.Date)! Reason: Validation failed for query for method public abstract java.util.List com.elijah.onlinebankingapp.repository.transaction.TransactionTypeRepository.findByBankAccountAndTransactionDate(com.elijah.onlinebankingapp.model.account.BankAccount,java.util.Date,java.util.Date)!; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.elijah.onlinebankingapp.repository.transaction.TransactionTypeRepository.findByBankAccountAndTransactionDate(com.elijah.onlinebankingapp.model.account.BankAccount,java.util.Date,java.util.Date)!
This is my TransactionType model class
#Entity
#NoArgsConstructor
#Data
public class TransactionType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date transactionDate;
private double amount;
private double currentBalance;
private String transactionType;
private String description;
private String depositorOrWithDrawalName;
#ManyToOne
#JoinColumn(name = "account_id")
private BankAccount bankAccount;
}
The BankAccount class
#Entity
#Data
#NoArgsConstructor
#Slf4j
public class BankAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String accountType;
private String accountStatus;
private String accountNumber;
private double currentBalance;
private LocalDate createdDate;
#OneToOne
#JoinColumn(name = "customer_id")
private Customer customer;
}
my BankAccountStatement class
#Data
#NoArgsConstructor
public class BankAccountStatement {
private double id;
private String transactionDate;
private String transactionType;
private String description;
private double amount;
private double currentBalance;
private String depositorOrWithDrawalName;
}
BankAccountStatementDto
#Data
#NoArgsConstructor
public class BankAccountStatementDto {
private Date startDate;
private Date endDate;
}
my repository in which I did the query
#Repository
public interface TransactionTypeRepository extends JpaRepository<TransactionType,Long> {
#Query("select t from TransactionType t where t.bankAccount =:bankAccount, t.transactionDate <=:startDate AND t.transactionDate >=:endDate")
List<TransactionType> findByBankAccountAndTransactionDate(BankAccount bankAccount, #Param("startDate") Date startDate,#Param("endDate") Date endDate);
}
my service class
#Service
public class TransactionTypeService {
#Autowired
private TransactionTypeRepository transactionTypeRepository;
#Autowired
private BankAccountService bankAccountService;
public List<BankAccountStatement> getUserAccountStatement(BankAccountStatementDto bankAccountStatementDto, String accountNumber) throws DataNotFoundException {
BankAccount bankAccount = bankAccountService.getAccountByAccountNumber(accountNumber);
List<TransactionType> transactionTypeList = transactionTypeRepository.findByBankAccountAndTransactionDate(bankAccount,bankAccountStatementDto.getStartDate(),bankAccountStatementDto.getEndDate());
//the TransactionType has so many data and I don't need all the data in it
//I only want to retrieve the important information and store on this BankAccountStatement
List<BankAccountStatement> bankAccountStatementList = new ArrayList<>();
BankAccountStatement bankAccountStatement = new BankAccountStatement();
for (TransactionType transactionType: transactionTypeList){
bankAccountStatement.setId(transactionType.getId());
bankAccountStatement.setTransactionType(transactionType.getTransactionType());
bankAccountStatement.setTransactionDate(transactionType.getTransactionDate().toString());
bankAccountStatement.setDescription(transactionType.getDescription());
bankAccountStatement.setDepositorOrWithDrawalName(transactionType.getDepositorOrWithDrawalName());
bankAccountStatement.setCurrentBalance(transactionType.getCurrentBalance());
bankAccountStatement.setAmount(transactionType.getAmount());
bankAccountStatementList.add(bankAccountStatement);
}
return bankAccountStatementList;
}
}
my controller
#RestController
public class TransactionController {
#Autowired
private TransactionTypeService transactionTypeService;
#GetMapping("/account/statement/from/enteredDate")
public ResponseEntity<List<BankAccountStatement>> getCustomerAccountStatement(#RequestBody BankAccountStatementDto bankAccountStatementDto,#RequestParam("accountNumber")String accountNumber) throws DataNotFoundException {
return new ResponseEntity<>(transactionTypeService.getUserAccountStatement(bankAccountStatementDto,accountNumber),HttpStatus.OK);
}
}

The problem is with your transactionDate field. You have to let the hibernate know whether it holds only date, only time, or both. Assuming you are using java.util.Date, it contains both date and time upto milliseconds. #Temporal annotation in sprint boot will let know the hibernate about what data it holds.
In your case, you should add #Temporal(TemporalType.TIMESTAMP) for transactionDate field (assuming you are using java.util.Date).
You have other temporal types as well,
TemporalType.DATE - only date, TemporalType.TIME - only time

I have solved the issue, the problem was from my query, I was using a comma after the bankAccount instead of AND.
This is the final solution in case anyone has such a problem
#Query("select t from TransactionType t where t.bankAccount = :bankAccount AND t.transactionDate <= :startDate AND t.transactionDate >= :endDate")
List<TransactionType> findByBankAccountAndTransactionDate(BankAccount bankAccount, #Param("startDate") Date startDate,#Param("endDate") Date endDate);

Related

Spring boot: How to Save data to specific DTYPE

I have this entity:
public class StatementLinesEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statementLinesId;
#CreationTimestamp
#Temporal(TemporalType.DATE)
private Date dateOperation;
private String operationNature;
private BigDecimal amount;
private String debitAmount;
And this entity has Inheritance of type SINGLE_TABLE:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class OperationCreditEntity {
#Id
#Column(nullable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
#CreatedDate
private Date operationDate;
#OneToOne
private StatementLinesEntity statementLine;
And these 3 enteties inherite of it :
#Entity
#DiscriminatorValue("Espece")
public class OperationEspecesEntity extends OperationCreditEntity {
private String cin;
private String nomEmetteur;
private String prenomEmetteur;
=============================
#DiscriminatorValue("Virement")
public class OperationVirementEntity extends OperationCreditEntity {
private String rib;
===========================
#Entity
#DiscriminatorValue("Cheque")
public class OperationChequeEntity extends OperationCreditEntity{
private int numeroCheque;
Let's suppose I have a List<StatementLinesEntity> consist of 2 lines, on line has debitAmount = C and operationNature = Virement and second line has debitAmount = C and operationNature = Espece. My goal is to persist each line in a specific DTYPE. example
first line should be persisted in OperationCreditEntity table DTYPE = Virement and the second should be persisted in OperationCreditEntity table DTYPE = Espece
The model to me should be more like:
#Entity
public class StatementLinesEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statementLinesId;
#CreationTimestamp
#Temporal(TemporalType.DATE)
private Date dateOperation;
#OneToOne(mappedBy = "statementLine")
private OperationCreditEntity operation;
private BigDecimal amount;
private String debitAmount;
}
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
abstract public class OperationCreditEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
#CreatedDate
private Date operationDate;
#OneToOne
private StatementLinesEntity statementLine;
}
Any method then that takes in StatementLinesEntity instances can then take in one that references an OperationCreditEntity instance (which can be any one of its subclasses). There is no need to manage, parse or handle String operationNature strings directly, as the operation type will determine the operation nature.
This might change other signatures, serialization (such as JSON though), so if you can't use this and are 'stuck' with your existing StatementLinesEntity data representation YOU need to handle how to create your OperationCreditEntity instances from that data. There is no tool to automatically do it for you. It is as simple as a utility of the form:
OperationCreditEntity createOperationInstance(StatementLinesEntity statementLine) {
String operationNature = statementLine.getOperationNature();
OperationCreditEntity returnVal = null;
if "Espece".equals(operationNature) {
returnVal = new OperationEspecesEntity();
} else if "Virement".equals(operationNature) {
returnVal = new OperationVirementEntity();
} else if "Cheque".equals(operationNature) {
returnVal = new OperationChequeEntity();
} else {
throw new IllegalStateException();
}
returnVal.setStatementLine(statementLine);
return returnVal;
}
Just call save using your OperationCreditEntity repository when ever you call this method to get it put into the same transactional context you are making changes to. Also note, those OperationCreditEntity subclasses have data you will need to find a way to fill in on your own; I personally think this data will likely be tied to data available when defining/creating a StatementLinesEntity, so should be generated/created then, not after the fact, but that is up to you.
Added just to be complete:
Yes, you can access the column used to store discriminator values directly in a base entity class. Nothing stops or prevents you from mapping the column as you would any other database column. For Hibernate, it uses "DTYPE", so
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class OperationCreditEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
#CreatedDate
private Date operationDate;
#Column(name="DTYPE",insertable=false, updatable=false)
private String typeValue;
}
Notice I marked this as insertable/updatable=false though. It is provider specific if it complains about controlling this value in this way; many try to do so with the hope of changing it. Changing an entity 'type' is not supported. A Caterpillar does not become a Butterfly just by changing a string value. Any caches that hold OperationCreditEntity or some specific subclass type aren't magically going to have the object type changed; JPA requires you to delete the entity and create a new instance (of the proper class) for that data, preferably after flushing the delete operation.
Also note, you can query and use Entity Type Expressions (TYPE) without having a column or other mapping for it.
"Select line from OperationCreditEntity operation join operation.statementLine line where TYPE(operation) IN (OperationEspecesEntity, OperationChequeEntity) and line.somethingElse = :someValue"

JPA: Many to many relationship - JsonMappingException: Infinite recursion

I'm having trouble with a many to many relation with JPA.
My code looks as follows:
The Sensor class:
#Entity
#Table(name = "sensor")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Sensor {
#Id
private long chipId;
#OneToMany(mappedBy = "sensor")
#JsonBackReference
private Set<Link> userLinks;
private String firmwareVersion;
private long creationTimestamp;
private String notes;
private long lastMeasurementTimestamp;
private long lastEditTimestamp;
private double gpsLatitude;
private double gpsLongitude;
private double gpsAltitude;
private String country;
private String city;
private boolean indoor;
private boolean published;
}
The user class:
#Entity
#Table(name = "user")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonManagedReference
private int id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user")
private Set<Link> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}
And the Link class (relation class):
#Entity
#Table(name = "link")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Link {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "user_id")
#MapsId("user_id")
private User user;
#ManyToOne
#JoinColumn(name = "sensor_id")
#MapsId("sensor_id")
private Sensor sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}
The controller:
...
#RequestMapping(method = RequestMethod.GET, path = "/user/{email}", produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Returns details for one specific user")
public User getUserByEmail(#PathVariable("email") String email) {
return userRepository.findByEmail(email).orElse(null);
}
...
The UserRepository:
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByEmail(String email);
#Modifying
#Query("UPDATE User u SET u.firstName = ?2, u.lastName = ?3, u.password = ?4, u.role = ?5, u.status = ?6 WHERE u.id = ?1")
Integer updateUser(int id, String firstName, String lastName, String password, int role, int status);
}
I want to achieve, that the user endpoint shows all linked sensors with that particular user.
What I get is only an error message:
JSON mapping problem:
com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"];
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Infinite
recursion (StackOverflowError) (through reference chain:
com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"])
How can I fix this issue?
Thanks in advance
Marc
------------------------------------ Edit -----------------------------------
According to Abinash Ghosh's answer, I added following DTOs:
UserDto:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserDto {
private int id;
private String firstName;
private String lastName;
private Set<LinkDto> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}
LinkDto:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class LinkDto {
private Integer id;
private SensorDto sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}
And the mapper (I realized it a bit different, but it should be the same):
public UserDto getUserByEmail(#PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
return convertToDto(user);
}
private UserDto convertToDto(User user) {
return mapper.map(user, UserDto.class);
}
This leads to following Exception:
2020-04-13 14:22:24.383 WARN 8176 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext#68ab57c7<rs=HikariProxyResultSet#2017009664 wrapping Result set representing update count of -1>
1) Error mapping com.chillibits.particulatematterapi.model.db.main.User to com.chillibits.particulatematterapi.model.io.UserDto
1 error] with root cause
java.lang.StackOverflowError: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.19.jar:8.0.19]
...
It's working!
This post helped: https://stackoverflow.com/a/57111004/6296634
Seems that you should not use Lombok #Data in such cases.
When User serialized for the response, all getter methods of User's fields are called.
So, User relational field sensorLinks's getter are also called to set value. This happened recursively. That's cause of infinite recursion.
It's better to not use Entity as a response. Create a DTO class for User then map User entity value into DTO then send response. Don't use any Enity class again into DTO then it will result same problem
For dynamically map one model to another you can use ModleMapper
public class UserDTO {
//Fields you want to show in response & don't use enity class
private Set<LinkDTO> sensorLinks;
}
public class LinkDTO{
//Fields you want to show in response &don't use enity class
}
public User getUserByEmail(#PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
UserDTO userDto = merge(user,UserDTO.class)
return userDto;
}
public static <T> void merge(T source, T target) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.map(source, target);
}

JPA generate fails with JSON

I cound't find any solution to manage that fail, so I decided to create a new question. I have a simple class
#Entity
public class Reservation {
// private Integer RESERVATION_ID;
// private Integer id;
private long code;
private Date date;
private Client reservationClient;
private WashType reservationWashType;
private Vehicle reservationVehicle;
private Wash reservationWash;
private Worker reservationWorkerPesel;
private Review reservationReview;
private ReservationReminder reservationReminder;
}
Where I run a query like that:
#Query("SELECT r FROM Reservation r JOIN FETCH r.reservationReminder where r.reservationWorkerPesel = :worker")
List<Reservation> findByReservationWorkerPesel(#Param("worker") Worker worker);
And at first I everything looks nice, but then I do some operations like that:
public List<ReservationReminder> findByReservationWorkerPesel(Worker worker) {
List<ReservationReminder> reservationReminderList = new ArrayList<>();
List<Reservation> byReservationWorkerPesel = reservationDao.findByReservationWorkerPesel(worker);
for (Reservation r : byReservationWorkerPesel) {
if (r.getReservationReminder() != null && r.getReservationReminder().getChecked() == false)
reservationReminderList.add(r.getReservationReminder());
}
return reservationReminderList;
}
And after that when I see how JSON looks like - it's strange, because:
[{"reservationReminderId":2,"reservation":{"code":263022,"date":1487851200000,"reservationClient":{"clientPesel":"91122619197","name":"Client 1","surname":"Client 1","email":"client#wp.pl","phone":"234567890","accountNumber":"34567897654345678987654356","clientUser":{"userId":3,"login":"client","passwordHash":"$2a$10$0jJMMzeh2CTRagk3hwRSlurx.mxLgR1aAUQOYBD9QFqbISeoTSVN.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":
....
[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":"$2a$10$6WrmwpwOdhv6UXBo2mYq8ucKiQTwvIwTHw23myo6.oujflh8pqKR.","userRole":{"roleId":3,"name":"CLIENT","users":[{"userId":8,"login":"clien5","passwordHash":{"timestamp":1489015140465,"status":200,"error":"OK","exception":"org.springframework.http.converter.HttpMessageNotWritableException","message":"Could not write content: Infinite recursion (StackOverflowError) (through reference chain: com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]-
...
\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"]->com.carwash.domains.Role[\"users\"]->org.hibernate.collection.internal.PersistentBag[0]->com.carwash.domains.User[\"userRole\"])","path":"/api/reservationreminder"}
What am I doing wrong?
Perhaps it can say you something - I don't why after making a GET method (only get) I got some those bugs?
You have a Infinite recursion between your User and UserRole object. Whenever a user is serialized his related user roles are also serialized. Since user roles does also have a relation back to the user you have the recursion.
Solution to this could be to use #JsonManagedReference (added to the relation in User) and #JsonBackReference (realtion at UserRoles). See also here: Infinite Recursion with Jackson JSON and Hibernate JPA issue
#Entity
public class User
...
#JsonManagedReference
private Set<UserRole> userRoles;
#Entity
public class UserRole
...
#JsonBackReference
private User user;
#JsonManagedReference would mean that during serialization the relation part is taken into account. So the related user roles would be also serialized. Since there the related connection is marked with #JsonBackReference serialization stops to go further.
#KLHauser
So how to manage the case if I have a class
#Entity
public class ReservationReminder {
private int reservationReminderId;
private Reservation reservation;
private boolean isChecked;
private Date checkedDate;
and Reservation class
#Entity
public class Reservation {
// private Integer RESERVATION_ID;
// private Integer id;
private long code;
private Date date;
private Client reservationClient;
private WashType reservationWashType;
private Vehicle reservationVehicle;
private Wash reservationWash;
private Worker reservationWorkerPesel;
private Review reservationReview;
private ReservationReminder reservationReminder;
#Entity
public class Worker {
private String workerPesel;
private String name;
private String surname;
private Date startDateWorking;
private String accountNumber;
private List<Review> workerReview;
private Adress workerAdress;
private List<LaborHistory> workerLaborHistory;
private Wash workerWash;
//private List<WorkerWorkerTime> workerWorkTime;
// private Role WORKER_ROLE;
private User workerUser;
private List<Reservation> workerReservation;
And I'd like to load ReservationReminder class with Worker from Reservation class? If I use #JsonIgonre like that
#OneToOne(mappedBy = "reservationReminder", fetch = FetchType.LAZY)
#JsonIgnore
public Reservation getReservation() {
return reservation;
}
I only got a Json with checkedDate, isChecked and reservationReminderId from ReservationReminder

Hibernate association mappings

I have two classes: Event and User. Every 'event' is created by only one 'user', but a 'user' can create many 'events'. I created relationships like this:
#Entity
#Table(name="events")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Event {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#NotBlank
private String name;
#NotBlank
#Type(type="text")
private String description;
private String event_type;
#NotNull
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime expected_start;
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime expected_end;
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime actual_start;
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime actual_end;
#NotBlank
private String environment;
private int executed_by;
#Temporal(TemporalType.TIMESTAMP)
#CreationTimestamp
private Date created_at;
#Temporal(TemporalType.TIMESTAMP)
#UpdateTimestamp
private Date updated_at;
#ManyToOne
#JoinColumn(name="created_by")
private User creator;
}
#Entity
#Table(name="users")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "username")
#FieldMatch(first = "password", second = "repassword",message = "The password fields must match")
public class User{
#Id
#NotBlank
#Size(min=5,max=15)
#Column(name="username", unique=true)
private String username;
#NotBlank
private String first_name;
#NotBlank
private String last_name;
#NotBlank
private String password;
#NotBlank
#Transient
private String repassword;
#NotBlank
private String email;
#NotBlank
private String phone;
#Temporal(TemporalType.TIMESTAMP)
#CreationTimestamp
private Date created_at;
#Temporal(TemporalType.TIMESTAMP)
#UpdateTimestamp
private Date updated_at;
#OneToMany(mappedBy="creator")
private Collection<Event> events=new ArrayList<Event>();
}
DAO:
public List<Event> getEvents() {
Criteria criteria=getCurrentSession().createCriteria(Event.class);
return (List<Event>) criteria.list();
}
I am using jackson for converting to JSON. when make an ajax call for a particular event, it pull the user information along with it. that's OK as it is need for me. But it also pull's other events created by user because of #oneToMany in user class. How to overcome this
I guess that Jackson, while serializing, checks the events collection size and, since you are using openSessionInViewFilter, the collection gets fetched, so it puts it in the response. You can just #JsonIgnore the events field.
Design note:
I think that "Open session in view" is an anti-pattern. Any transaction management should be done a level below the view layer. In order to decouple view layer from the layers below you should be returning DTOs to the view layer instead of JPA entities. Unless you're doing some toy project, that's the investment you won't regret.
How I do it:
I use select new mechanism from JPA to write dedicated query
to make queries typesafe I use QueryDSL (but that's optional, you can try out an ordinary JPQL first)
queries return DTO objects that don't even need Jackson annotations as they represent what I want to get in the view.
This approach has additional advantage of being much faster than normal entity loading by JPA. Some prefer loading entities and then repackage them which saves effort required to write the query (which is minimal once you get it how it works) but then there is no performance benefit.

Setting relationship in Model class does not create db table entries in playFramework

I create 3 model classes in playframework, and set one-one relationship and one-to many relationship in one of the class. The code snippet are as follows:
Person.java
///////////////////////
#Entity
#Table(name = "person")
public class Person extends Model{
#Id
private Long id;
private String lastName;
private String firstName;
private String userId;
private Address address;
private List<Task> tasks;
....
}
Task.java
//////////////////////////
#Entity
#Table(name = "task")
public class Task extends Model{
#Id
private Long id;
private String name;
private String descript;
private String details;
...........
}
Address.java
////////////////////
#Entity
#Table(name = "address")
public class Address extends Model{
#Id
private Long id;
private String street;
private String state;
.........
}
I create person object and set the attributes/one-one/one-many relationships.
I try to save the person object with both attributes and relationships to mysql db by calling person.save().However, it ends up saving only attributes userId/firstName/LastName. the address object and tasks list object are not saved in db.
My question is : is there any way to save the relationship objects in db long with person.save()? that is , after calling person.save(), address table and task table create new entires corresponding to them.
I end up setting foreign keys in person class to handle this relationship manually.
Thanks in advance!
You might want to have a look at a JPA tutorial in particular topics about #OneToMany and #OneToOne annotations. I'd recommend https://en.wikibooks.org/wiki/Java_Persistence/OneToMany and https://en.wikibooks.org/wiki/Java_Persistence/OneToOne.