JPA query couldn't find the right constructor - java

So basically I try to get some data of a User by its ID and it has a field with #OneToOne relationship(bidirectional)
When I try to create a query in JpaRepository
with #Query annotation
#Query("SELECT new com.project.model.user(u.id, u.username, u.profile.last_online, u.profile.about) FROM User u WHERE id = :id)
and a constructor
User(long id, long username, Profile profile) {
this.id = id;
this.username = username;
this.profile = profile;
}
it still can't really find the constructor and says that there is no "matching constructor", is there a way to create the constructor better?

The constructor you expect in your query has (long, String, Timestamp, String). And your constructor takes (long, long, Profile).
So you need to create a matching constructor:
public User(long id, String username, Timestamp lastOnline, String about) {
this.id = id;
this.username = username;
this.profile = new Profile(lastOnline, about); // create new Profile with lastOnline & about
}
This should work.
But may I ask why you're not using the JpaRepository#findById(Long id) method?

Related

spring boot Cannot invoke Repository because Repository is null

public class UserList {
private String id;
private String email;
private String userType;
private String rolls;
private String partner;
private Integer customersLinked;
private String position;
private String status;
#Autowired
ICustomerRepository customerRepository;
public UserList (Users user){
this.id = user.getId();
this.email = user.getEmail();
this.userType = user.getUserType();
this.rolls = user.getRolls();
this.partner = user.getPartner();
List<Customer> customersLinked = customerRepository.findAllByLinkedUsersIn(user.getId());
this.customersLinked = 0;
this.position = user.getPosition();
this.status =user.getStatus();
}
//Getter and Setter
}
This class is used as a list in the frontEnd ,get specific data ,not send all the data
#RequestMapping(value = "usersLinked/{id}/{type}", method = RequestMethod.GET)
public Object getUsersLinkedById(#PathVariable("id") String id,#PathVariable("type") Integer type) {
List<String> users = null;
switch (type) {
case 0:
users = usersRepository.findAll().stream().map(m -> m.getId()).collect(Collectors.toList());
break;
}
//Add userList
List<UserList> userList = new ArrayList<>();
if(users != null)
{
users.forEach(userId ->
{
Optional<Users> user = this.usersRepository.findById(userId);
userList.add(new UserList(user.get()));
});
}
return userList;
}
}
As you can see from above I am calling al the data from the user repository and sending it the list
My customer repository
public interface ICustomerRepository extends MongoRepository<Customer, String> {
Customer findByBusinessInformation_businessName(String businessName);
List<Customer> findByBusinessInformation_partnerAssigned(String partnerAssigned);
#Query("{ 'LinkedUsers' : ?0 }")
Customer findByLinkedUsers(String id);
List<Customer> findAllByLinkedUsersIn(String id);
}
In the userList I get the error when I add the logic wityh the customerRepository ,without the repository there everything is working(Want to use the repository to get an array of customer and then get the size() of the array and add it to linkedCustomers). Am I missing sommething
You are trying to inject the field customerRepository using Autowired annotation, but your class is not injectable.
You can add an annotation #Repository on your class UserList
Or use constructor injection (better way to inject your beans)
You're probably missing the #repository annotation on top of your repository class.
Another unrelated word of advice:
In your controller you use findAll and filter in java to keep only the ids.
Then you go to the same repository and perform another query per user-id from above.
This is a causing you to create multiple database calls which are one of the most expensive operations you can do, when you already have all your data from the first single query...
Also if you're only looking at the bottom part of the function you don't event need a query per each user-id (when you have a list of user ids as input), you can create a query that uses the 'in' convention and pass a list of user-ids to create a single db call.
First of all I would get rid of #Autowired ICustomerRepository customerRepository; in UserList class. It doesn't belong there. The counting of linked customers should be executed in ICustomerRepository and the result to be passed into UserList via the constructor.
e.g.
public class UserList {
private String id;
private String email;
private String userType;
private String rolls;
private String partner;
private Long customersLinked; //better use Long instead of Integer
private String position;
private String status;
// constructor takes the number of linked customers as parameter
public UserList (Users user, Long customersLinked ) {
this.id = user.getId();
this.email = user.getEmail();
this.userType = user.getUserType();
this.rolls = user.getRolls();
this.partner = user.getPartner();
this.customersLinked = customersLinked;
this.position = user.getPosition();
this.status =user.getStatus();
}
//Getter and Setter
}
and then create the count query in ICustomerRepository
e.g.
public interface ICustomerRepository extends MongoRepository<Customer, String> {
//other methods
Long countByLinkedUsersIn(String id); //not so sure if this query works in mongo
}
and finally in your controller
Optional<Users> user = this.usersRepository.findById(userId);
Long count = this.usersRepository.countByLinkedUsersIn(userId);
userList.add(new UserList(user.get(), count));
P.S. I have a doubt for the query method: Long countByLinkedUsersIn(String id);. Usually when repository methods have "In" in their names, countByLinkedUsersIn, then it is expected as parameter a List and not a single user id. However if your previous method List<Customer> findAllByLinkedUsersIn(String id); worked for you, then this one should work too.

getting error PLS-00221 is not a procedure or is undefined when calling a stored procedure in springboot jpa repository

please help me with this one. I have a very simple crud project with a student table in oracle DB(ID,name,age,email) in springboot and all i want to know is get a student with a method in the jpa repository that calls a stored procedured. when run the project i got an error PLS-00221 is not a procedure or is undefined.
--stored procedure
CREATE OR REPLACE FUNCTION
findstudentbyid
RETURN STUDENT%ROWTYPE
IS
estudiante STUDENT%ROWTYPE;
BEGIN
SELECT *
INTO estudiante
FROM STUDENT
WHERE ID=1;
RETURN estudiante;
END findstudentbyid;
//Entity in sprongboot project
#Entity
#Table
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(
name = "findstudentbyid",
procedureName = "findstudentbyid"
)
})
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
//Private variables
private Long ID;
private String name;
private Number age;
private String email;
//Constructor
protected Student(){}
public Student(String name , Number age , String email){
this.name = name;
this.age = age;
this.email = email;
}
public Long getID() {
return ID;
}
public String getName() {
return name;
}
public Number getAge() {
return age;
}
public String getEmail() {
return email;
}
}
//JPA CRUD REPOSITORY
public interface StudentRepository extends CrudRepository<Student,Long>{
#Procedure(name = "findstudentbyid")
Iterable<Student> findstudentbyid();
}
You create a function but not a stored procedure.
These objects are different for DB and JPA, try to change your create function to create procedure or change the procedure's call to the function's call signature.
Also, see here for more info about JPA and function call.

Issue creating users on simple spring boot crud api

After first starting the api, the first 3 user creation Post requests fail (screenshot at bottom of post) with the below error (unique constraint vilocation).
Subsequent requests work, with the first created user having an id of 4, then 5, etc...
How can I make the user creation work on the first (3) tries?
I suspect this relates to the pre-seeding of my users, which I'm doing with the below script. Possibly the auto ID generation first tries 1,2,3 -- which are already in use?
INSERT INTO user
VALUES (1, 'user1', 'pass1', 'ADMIN');
INSERT INTO user
VALUES (2, 'user2', 'pass2', 'USER');
INSERT INTO user
VALUES (3, 'user3', 'pass3', 'ADMIN')
could not execute statement; SQL [n/a]; constraint [\"PRIMARY KEY ON
PUBLIC.USER(ID)\"; SQL statement:\ninsert into user (name, password,
role, id) values (?, ?, ?, ?) [23505-196]]; nested exception is
org.hibernate.exception.ConstraintViolationException: could not
execute statement",
#RestController
public class UserResource {
#Autowired
private UserRepository userRepository;
#GetMapping("/users")
public List<User> retrievaAllUsers() {
return userRepository.findAll();
}
#DeleteMapping("/users/{id}")
public void deleteUser(#PathVariable Long id) {
userRepository.deleteById(id);
}
#PostMapping("/users")
public ResponseEntity<Object> createUser(#RequestBody User user) {
User savedUser = userRepository.save(user);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
}
-
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue
private Long id;
private String name;
private String password;
#Enumerated(EnumType.STRING)
private Role role;
public User() {
super();
}
public User(Long id, String name, String password, Role role) {
this.id = id;
this.name = name;
this.password = password;
this.role = role;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
edit - added role class
public enum Role {
USER, ADMIN
}
If you are using AUTO_INCREMENT in the column definition, then try changing strategy from GenerationType.AUTO to GenerationType.IDENTITY.
I noticed a similar behavior when I upgraded a project from Spring Boot 1.5 to 2.0.
Just an assumption. Firstly you are insterting datas with sql , but in your code you are creating new user and saving to db. So this new creation gives id as 1. But your db has a user record which primary key is as 1. Please remove all values from db and create your records from rest controller.
In my opinion, use a sequnce like this ,dont forget to create sequence in db;
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_generator")
#SequenceGenerator(name="user_generator", sequenceName = "user_seq", allocationSize=50)
Or read this and choose for your problem to solve.
AUTO: Hibernate selects the generation strategy based on the used
dialect,
IDENTITY: Hibernate relies on an auto-incremented database column to
generate the primary key,
SEQUENCE: Hibernate requests the primary key value from a database
sequence,
TABLE: Hibernate uses a database table to simulate a sequence.
PS: Identity should be more relevant but try others.
you should provide the column names also to make sure it's ordered.
INSERT INTO user (id, name, password, role)
VALUES (1, 'user1', 'pass1', 'ADMIN');
INSERT INTO user (id, name, password, role)
VALUES (2, 'user2', 'pass2', 'USER');
INSERT INTO user (id, name, password, role)
VALUES (3, 'user3', 'pass3', 'ADMIN')

How to get returned object from JPA EntityManager as customized bean

I am trying to join 3 tables to get required info using entityManager.createQuery.
Code littlepet as follows:
List<Object[]> o=entityManager.createQuery("SELECT u.loginId,ui.emailId,u.userId,ui.firstName,up.password,ui.phoneNunber,u.roleTypeId" + " From Users as u,UserInfo ui, UserPassword as up where u.userId = up.userId " + "AND u.userId=ui.userId").getResultList();
I have a bean with the above returned fields UserDetails.
public class UserDetails
{
String LoginId;
String Email;
String UserId;
String FirstName;
String Password;
String Mobile;
String RoleTypeId;
int Status;
getters() & setters()
}
But when I am trying to use List of UserDetails instead List of Object[] it says ClassCastException.
How to get/convert the returned object with customized bean?
List<UserDetails> o=entityManager.createQuery("SELECT new package.UserDetails( u.loginId,ui.emailId,u.userId,ui.firstName,up.password,ui.phoneNunber,u.roleTypeId)" + " From Users as u,UserInfo ui, UserPassword as up where u.userId = up.userId " + "AND u.userId=ui.userId").getResultList();
You need to have constructor with parameters in the same order as the fields are selected in the query, thus UserDetails(loginId, emailId, userId, firstName, password, phoneNunber, roleTypeId)
Or you can use your code and then write a converter which converts an array of objects into UserDetails.
List<Object[]> retrievedObjects = //yourCode.
for (Object[] objs : retrievedObjects) {
//write convert method which populates **UserDetails** fields from the objects.
UserDetails ud = convert(objs);
}
You can use a result transformer. Result transformer is a Hibernate feature (not JPA). So you need to use Session or Query from Hibernate.
Something like this:
List<UserDetails> result = entityManager.createQuery(
"SELECT u.loginId as loginId, ui.emailId as emailId ..."
).unwrap(org.hibernate.query.Query.class)
.setResultTransformer(new AliasToBeanResultTransformer(UserDetails.class))
.list();
You need to have aliases in the HQL: u.loginId as loginId and corresponding properties in the UserDetails:
class UserDetails
{
private String loginId;
public String getLoginId() {
return loginId;
}
public void setLoginId(String loginId) {
this.loginId = loginId;
}
}
Examples of transformers:
http://jpwh.org/examples/jpwh2/jpwh-2e-examples-20151103/examples/src/test/java/org/jpwh/test/querying/advanced/TransformResults.java

Override Pageable findAll for selecting fewer columns in Spring Data Rest

How to Override spring data repository to select only selected columns when going to pages that are discovered from /api page in spring data rest.
I added findAll as below -
public interface UserRepository extends BaseRepository<User, Integer>, UserRepositoryCustom {
#Query("select u from User u where email = :email and password = :password")
#Cacheable(value = "user-cache", key = "#user.login")
#RestResource(exported = false)
public User findUserByEmailAndPassword(#Param("email") String email, #Param("password") String password);
#RestResource(rel = "byEmail", path = "byEmail")
public User findUserByEmail(#Param("email") String email);
#RestResource(rel = "byPhone", path = "byPhone")
public User findUserByPhone(#Param("phone") String phone);
#Override
#Query("select u.id,u.email,u.phone from User u ")
public Page<User> findAll(Pageable pageable);
}
/api/users is giving an error -
{"cause":null,"message":"PersistentEntity must not be null!"}
I created a UserSummaryProjection class in same package as User
#Projection(name = "summary", types = User.class)
public interface UserSummaryProjection {
Integer getId();
String getEmail();
}
Then, going at /api/users or /users/3?projection=summary gives me desired result without changing the Repository.
Selecting subelements of User and still creating a User is somewhat counterintuitive.
I would create another entity for example UserDetails, that will be mapped by the same table with the same mapping.
public class UserDetails {
private int uid;
private String email;
private String phone;
}
And create a Repository, based on this new Entity.

Categories

Resources