Insert the table elements in Collection - mysql

HI,
I have a collection and i wanna insert all the elements of my table.
How i can do this operation in EJB QL?
For example: (this isn't my code)
I have my Collection:
Collection<Person> coll = new Collectio<Person>
And i have my table Person
#entity
private class Person{
private id;
private name;
//getters setter
}
I have popolate the table and i wanna all the element in my collection.
Thanks.

Introduce Query in your JPA entity:
#Entity
#NamedQuery(name = "Person.findAll", query = "SELECT p from Person p")
public class Person {
private id;
private name;
//getters setter
}
Then inject EntityManager in your code and use query:
public class MyClass {
#PersistenceUnit(name = "MyEntitiesFromPersistenceXML")
private EntityManagerFactory emf;
public void myMethod() {
EntityManager entityManager = emf.createEntityManager();
Query query = entityManager.createNamedQuery("Person.findAll");
#SuppressWarnings("unchecked")
List<Person> persons = query.getResultList();
// query returns List which, in turn, extends Collection
}
}
Btw, why your class is private? It must be public.

Related

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);
}

Creating a custom query with Spring DATA JPA?

I'm working on a project with Spring Data JPA. I have a table in the database as my_query.
I want to create a method which takes a string as a parameter, and then execute it as a query in the database.
Method:
executeMyQuery(queryString)
As example, when I pass
queryString= "SELECT * FROM my_query"
then it should run that query in DB level.
The repository class is as follows.
public interface MyQueryRepository extends JpaRepository<MyQuery, Long>{
public MyQuery findById(long id);
#Modifying(clearAutomatically = true)
#Transactional
#Query(value = "?1", nativeQuery = true)
public void executeMyQuery(String query);
}
However, it didn't work as I expected. It gives the following error.
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''select * from my_query;'' at line 1
Is there any other way, that I could achieve this goal?
The only part of it you can parameterise are values used in WHERE clause. Consider this sample from official doc:
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
Using EntityManager you can achieve this .
Suppose your entity class is like bellow:
import javax.persistence.*;
import java.math.BigDecimal;
#Entity
#Table(name = "USER_INFO_TEST")
public class UserInfoTest {
private int id;
private String name;
private String rollNo;
public UserInfoTest() {
}
public UserInfoTest(int id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, precision = 0)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "roll_no", nullable = true)
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
And your query is "select id, name from users where roll_no = 1001".
Here query will return an object with id and a name column. Your Response class is like below:
Your Response class is like:
public class UserObject{
int id;
String name;
String rollNo;
public UserObject(Object[] columns) {
this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
this.name = (String) columns[1];
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
here UserObject constructor will get an Object Array and set data with the object.
public UserObject(Object[] columns) {
this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
this.name = (String) columns[1];
}
Your query executing function is like bellow :
public UserObject getUserByRoll(EntityManager entityManager,String rollNo) {
String queryStr = "select id,name from users where roll_no = ?1";
try {
Query query = entityManager.createNativeQuery(queryStr);
query.setParameter(1, rollNo);
return new UserObject((Object[]) query.getSingleResult());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
Here you have to import bellow packages:
import javax.persistence.Query;
import javax.persistence.EntityManager;
Now your main class, you have to call this function. First get EntityManager and call this getUserByRoll(EntityManager entityManager,String rollNo) function. Calling procedure is given below:
Here is the Imports
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
get EntityManager from this way:
#PersistenceContext
private EntityManager entityManager;
UserObject userObject = getUserByRoll(entityManager,"1001");
Now you have data in this userObject.
Note:
query.getSingleResult() return a object array. You have to maintain the column position and data type with query column position.
select id,name from users where roll_no = 1001
query return a array and it's [0] --> id and 1 -> name.
More info visit this thread .
There is no special support for this. But what you can do is create a custom method with a String parameter and in your implementation get the EntityManager injected and execute it.
Possibly helpful links:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
How to access entity manager with spring boot and spring data
Note: I would reconsider if what you are trying to do is a good idea because it bleeds implementation details of the repository into the rest of the application.
if you want to add custom query you should add #Param
#Query("from employee where name=:name")
employee findByName(#Param("name)String name);
}
this query will select unique record with match name.this will work
Thank you #ilya. Is there an alternative approach to achieve this task using Spring Data JPA? Without #Query annotation?
I just want to act on this part. yes there is a way you can go about it without using the #query annotation. what you need is to define a derived query from your interface that implements the JPA repository instance.
then from your repository instance you will be exposed to all the methods that allow CRUD operations on your database such as
interface UserRepository extends CrudRepository<User, Long> {
long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
}
with these methods spring data will understand what you are trying to archieve and implement them accordingly.
Also put in mind that the basic CRUD operations are provided from the base class definition and you do not need to re define them. for instance this is the JPARepository class as defined by spring so extending it gives you all the methods.
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
}
For more current information check out the documentation at https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Based on #jelies answer, I am using the following approach
You can create another interface for your custom methods (as example MyQueryCustom) and then implement it as follows.
public class MyQueryRepositoryImpl implements MyQueryRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
public int executeQuery(String query) {
return entityManager.createNativeQuery(query).executeUpdate();
}
}
This will execute a custom query.

Orika Bean Mapper Spring Data ManyToMany join table overriding

In my Spring project I have domain and model layer separately. Domain layer are just entities, that maps to tables in MySQL db. Models are used in service layer. I have two tables: User and Roles, that have ManyToMany relationship.
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy="users", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private List<RoleEntity> roles;
//..Getters Setters
}
#Entity
#Table(name = "role")
public class RoleEntity {
#Id
#GeneratedValue
private Long id;
private String value;
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "role_id") , inverseJoinColumns = #JoinColumn(name = "user_id") )
private List<UserEntity> users;
}
Model
public class User {
private Long id;
private String name;
private List<Role> roles;
}
public class Role {
private Long id;
private String value;
private List<User> users;
}
Repository Layer
#Repository
public interface UserRepo extends JpaRepository<UserEntity, Long>{
}
Service
#Service
public class UserService {
#Autowired
private UserRepo userRepo;
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
public void save(User user) {
userRepo.save(mapper.map(user, UserEntity.class));
}
public User getUser(Long id) {
return mapper.map(userRepo.findOne(id), User.class);
}
}
The problem is - each time I retrieve user, and update it properties (name for example), and than save back it into table, row in the join table user_role, overrides too. For example if it was
id=1, user_id=1, role_id=1
, than after update it becomes
id=2, user_id=1, role_id=1
.
#Test
public void contextLoads() {
User user = userService.getUser(1L);
user.setName("kyk");
userService.save(user);
}
It works correct without mapper, so mapper is the reason. And I can't get any workaround. Has any one faced the same problem? Will be thankful for any help.

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.

Persisting with AngularJS and JPA

I'm trying to develop an application using Google Guice, JPA (EclipseLink) and AngularJS.
Basically in this application I have some Employees and more than one Salary for each of them.
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="ID_EMPLOYEE")
private long idEmployee;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee",targetEntity = Salary.class,fetch = FetchType.EAGER)
private List<Salary> salaries;
and the Salary object:
public class Salary implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="ID_SALARY")
private long idSalary;
#Column(name="SALARY_PA")
private String salaryPa;
#ManyToOne
#JoinColumn(name="ID_EMPLOYEE", referencedColumnName = "ID_EMPLOYEE")
private Employee employee;
Now, I'm able to insert, using angularJS and REST services, a new employee without any problem. What I can't do is adding a new salary to an existing employee.
Basically what I have in my angularJS controller is:
$scope.employee.salaries.push(this.salaryToAdd);
employeeServ.persistSalary(employee);
and then I just save the employee. The salary is saved but when I try to get the Employee sometimes I can see the salary sometime not. What I noticed is that the salary doesn't have any reference to the Employee so maybe when I get the employee JPA doesn't know if the salary is related to that Employee.
This is my dao and the part related to the persistence:
public abstract class GenericDao<E> implements AbstractDao<E> {
#Inject
private EntityManager em;
protected abstract Class<E> getGenericClass();
#Transactional
#Override
public void insert(E entity) {
em.persist(entity);
}
#Transactional
#Override
public void update(E entity) {
em.merge(entity);
}
#Transactional
#Override
public void delete(E entity) {
em.remove(entity);
}
public E findById(Long id) {
Class<E> clazz = getGenericClass();
return em.find(clazz, id);
}
public List<E> findAll() {
Class<E> clazz = getGenericClass();
CriteriaQuery<E> query = em.getCriteriaBuilder().createQuery(clazz);
Root<E> root = query.from(clazz);
query.select(root);
return em.createQuery(query).getResultList();
}
public EntityManager getEntityManager() {
return em;
}
#Path("/employee")
public class EmployeeProvider {
#Inject
EmployeeDao dao;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/get/{id}")
public String get(#PathParam("id") String id) {
final Gson gson = new GsonBuilder().setPrettyPrinting().create();
if("all".equals(id)) {
return gson.toJson(dao.findAll().toArray());
} else {
return gson.toJson(dao.findById(Long.valueOf(id)));
}
}
#POST
#Path("/post")
public void post(String employee) {
final Gson gson = new GsonBuilder().setPrettyPrinting().create();
Employee entity = gson.fromJson(employee, Employee.class);
dao.update(entity);
}