I have this entities:
public class AnswerEntity {
#ManyToOne
private UserEntity user;
#ManyToOne
private AnswerDirectoryEntity answer;
#ManyToOne
private QuestionEntity question;
}
public class QuestionEntity {
#ManyToOne
private QuestionnaireEntity questionnaire;
}
public class QuestionnaireEntity {
private String code;
}
I need to take all user answers by user ID and corresponding code from QuestionnaireEntity.
I do it by create query like this:
List<AnswerEntity> answerList = answerRepository.findAllByUserId(userId);
and iterate over each object in my list and with using equals I compare each object to my questionnaire code:
for(AnswerEntity answerEntity : answerList){
if(answerEntity.getQuestion().getQuestionnaire().getCode().equals(questionnaireId)){
///
}
but this solution is very slow because it must iterate each object from my database,
can anybody tell my how to create an query in my repository which can help me?
You can use JPA method query this way in repository
List<AnswerEntity> findByUserIdAndQuestionQuestionnaireCode(Integer userId, String code);
Related
In my Spring boot batch application, I am calling a JPA repository class from Tasklet.
The JPA call retrieves a particular value (Entity object) from DB. The problem is, If I update some value in the entity object, once the control goes out of Tasklet, it automatically updates to DB even though I am not calling any save operation. How to prevent this? Default JPA implementation is Hibernate.
Tasklet class
Employee employee = employeeRepository.fetchEmployee(employeeName);
List<Address> addressList = employee.getAddress();
addressList.forEach(e -> e.setStatus(Status.INVALID.toString()));
Repository
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
#Query("select em from Employee em where em.employeeName = :employeeName")
public Employee fetchEmployee(#Param("employeeName") Long employeeName);
}
Entity class
#Entity
#Table(name = "Employee")
public class Employee implements java.io.Serializable {
private static final long serialVersionUID = -3769636546619492649L;
private Long id;
private List<Address> address;
private String employeeName;
// Getters and setters
// #OneToMany mapping to Address
}
Even though I am not calling a .save() operation, it automatically updates Address table Status to "INVALID"
This happen because the entity is not in detached state. In EJB we can do this in the following way.
EJB solution
#Query(value = "select * from Employee WHERE EmployeeName = ?1", nativeQuery = true)
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<Employee> fetchEmployee(String employeeName);
This will make the transaction closed. Changes you make to entity will not get saved in DB
Spring JPA
After a bit of research i found JPA doesn't provide the detach functionality out of the box.
Refer : https://github.com/spring-projects/spring-data-jpa/issues/641
To make it work we can have a custom JPA repository which overrides detach method. An example is given in this link.
https://www.javaer101.com/en/article/1428895.html
Use Deep cloning to solve your issue.
First override the clone method inside your Address class like below.
Note : Please customize the implementation of clone() method by adding your class attributes.Since you didn't mention the structure of the class Address , I have implemented the solution with my own defined class attributes.
Address class
public class Address {
private String country;
private String city;
private String district;
private String addressValue;
public Address() {
super();
}
public Address(String country, String city, String district, String addressValue) {
super();
this.country = country;
this.city = city;
this.district = district;
this.addressValue = addressValue;
}
//Getters and Setters
#Override
protected Object clone() {
try {
return (Address) super.clone();
} catch (CloneNotSupportedException e) {
return new Address(this.getCountry(), this.getCity(), this.getDistrict(),this.getAddressValue());
}
}
}
Then re construct your class Tasket like below.
Tasket Class
Employee employee = employeeRepository.fetchEmployee(employeeName);
List<Address> addressList = employee.getAddress();
List<Address> clonedAddressList = new ArrayList<>();
addressList.forEach(address -> clonedAddressList.add((Address)address.clone()) );
clonedAddressList.forEach(address -> address.setStatus(Status.INVALID.toString()));
I have a User object, and a Ticket object that have a ManyToMAny relationship
class User{
private Long id;
#ManyToMany
private Set<Ticket> tickets;
}
class Ticket{
#ManyToMany
private Set<User> users;
}
Obviously this is a very simplified pseudo-like version of the code, but what would i name the method in my JPA Repository, to get all of the tickets that have a user with the specified ID in it? Is this possible, or should I make a custom query?
You can write 2 different named queries:
public interface TicketRepository extends JpaRepository<Ticket, Long> {
List<Ticket> findAllByUsers(User user);
List<Ticket> findAllByUsersIdIn(List<Long> userIds);
}
Method findAllByUsers(..) takes User object to search and return results, method findAllByUsersIdIn(..) takes user ids to search and return results.
I have an interface which is extending crud repository
public interface PersonRepo extends CrudRepository<Person, String> {
#Query(value="select name from PERSON where addr=?1", nativeQuery = true)
List<Person> getPeronUsingAddress(String addr);
}
Person entity looks like this:
class Person {
private String name;
private String phoneNumber;
private String address;
//along with getters setters and all basic hibernate annotation to persist and retrieve
}
the person object is saved into the databse and at the time of retrieving the native query is working fine as hibernate executes correct query. But I am not able to get the return type.
If the return type is List of Person then I am getting InvalidDataAccessResourceUsageException
If I create an interface and use the list of interface as return type like
interface response {
String getName();
}
List of Response interface getPeronUsingAddress(String addr);
then I am getting proxy object in the service. I am not able to get the datas from proxy object.
Another approach I did is to use List of object as return type. But it is not possible to downcast to my Person Object.
How to do that.? Or is there any other solution by which I can return selective columns from crud repository and get a Java object with those selected Columns.
In order to fetch selected columns from an entity, you can do like below :
class Person {
private Integer id;
private String name;
private String phoneNumber;
private String address;
//along with getters setters and all basic hibernate annotation to persist and retrieve
}
Create a DTO or Java Object like below :
public class PersonDTO {
private Integer id;
private String name;
private String phoneNumber;
private String address;
public PersonDTO(Integer id, String name, String phoneNumber, String address) {
// logic here
}
//If you want just want name and phone number.
public PersonDTO(String name, String phoneNumber) {
// logic here
}
// you can't create overridden constructors as all members are of same type and at runtime program won't be able to differentiate unless you provide some logic for it.
// getters, setters, any other methods here...
}
Now below will be you Query but it's not native, if you want to keep native query then you will need to use ResultTransformer like here
#Query("select new your.package.PersonDTO(p.name, p.phoneNumber) from Person p where p.id = :id")
public PersonDTO getPersonById(Integer id);
So i have following Entitys/Tables for a many to many relation: Satz, Track and the mapping Table Trackliste
#Entity
class Track{
// name, id
#ManyToMany(targetEntity = Satz.class, fetch = FetchType.LAZY)
#JoinTable(
name="trackliste", joinColumns=#JoinColumn(name="TrackID"),
inverseJoinColumns=#JoinColumn(name="SatzID"))
private Set<Satz> saetze;
// getters and setters
}
#Entity
class Trackliste {
// id, trackid, satzid.
// getters and setters
}
#Entity
public class Satz implements Serializable {
// id, titel, werkId, etc
#ManyToMany(mappedBy="saetze", fetch = FetchType.LAZY)
private Set<Track> tracks;
// getters and setters
}
and my repository looks like this:
public interface SatzRepository extends CrudRepository<Satz, Integer> {
List<Satz> findById(int id);
List<Satz> findByWerkId(int id);
//Some query maybe?
//List<Satz> findByTracks(String name);?
}
The Mapping works so far, when i call my Webservice it returns a json object and with the help of debugging i can see that the SatzRepository Set Contains objects of Track.
Now comes my question: How do i return a Satz based on the given Track name is this possible? Lets say i have a URL like this: localhost:8080/rest/satz/trackname?name=%trackname%
If you need more Information please tell me.
you can add a method in TrackRepository to find track by name then you can get satz list from track object.
public interface TrackRepository extends CrudRepository<Track, Integer> {
Track findByName(String name);
}
#Transactional
public TrackServiceImpl implement TrackService{
#AutoWired
TrackRepository trackRepository;
List<Satz> getSatzByTrackName(String name){
Track track = trackRepository.getByName(name);
return track != null ? track.getSaetze() : new ArrayList<>();
}
}
I have 2 entities whose extracts are like these:
public class Visita {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="pdv_uid")
private PuntoDeVenta pdv;
}
public class PuntoDeVenta {
private Integer idclient;
private String zona;
#ManyToOne
#JoinColumn(name="pdv_categoria_uid", nullable=true)
private PuntoDeVentaCategoria categoria;
}
public class PuntoDeVentaCategoria {
private String descripcion;
}
I try to do restrictions with the differents fields and some of them work, some of them don't.
With this root criteria:
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Visita.class).createAlias("pdv", "pdv");
I try to make restrictions with the differents fields of "PuntoDeVenta" and "PuntoDeVentaCategoria" (with and without the createAlias) and I get the exception "could not resolve property", for example:
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Visita.class).createAlias("pdv", "pdv").add(Restrictions.eq("pdv.categoria.descripcion", "example"));
I've researching and I didn't found anything for my case.
Thank you in advance