Spring Boot JPA returns List of List not List of Object - spring

I am new to spring boot jpa. I would like to get return type as List but I am getting just List
My Entity Class
#Component
#Entity
#Table(name = "USERDB.USERS")
public class User() {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQ")
#SequenceGenerator(sequenceName = "MY_SEQ_NAME", allocationSize = 1), name = "MY_SEQ")
#Column(name = "userId")
private long id;
#Column(name = "firstName")
private String fName;
#Column(name = "midName")
private String mName;
#Column(name = "lastName")
private String lName;
#Column(name = "email")
private String email;
#Column(name = "createdDate")
private Timestamp createdOn;
public User() {
this.createdOn = new Timestamp(System.currentTimeMillis()
}
//SETTERS & GETTERS
}
My Repository;
public interface UserRepository extends JpaRepository<User, String> {
#Query("SELECT id fName, lastName, email FROM User u WHERE u.fName=(:fName)")
public List<User> findByEmail(#Param("fName") String fName);
}
All I wanted is to get a json response as a User Array with key value pair like below
[
[
"id": 1001,
"fName": John",
"lName": "Doe",
"email": "johnd#example.com"
],
[
"id": 1002,
"fName": "John",
"lName": "Simmons",
"email": "johns#example.com"
],
]
but I am getting a list with just values as below.
[
[
1001,
"John",
"Doe",
"johnd#example.com"
],
[
1002,
"John",
"Simmons",
"johns#example.com"
],
]
I am not sure where I am doing wrong or is this how I am supposed to get ? This is a hypothetical example of my actual program. Please excuse me for any errors.
Here is my controller class
#Restcontroller
public class UserController {
#Autowired
UserRepository repo;
#GetMapping("/user/{fname}")
public List<User> getUserByName(
#PathVariable("fname") String fname) {
return repo.findByEmail(fname);
}
}

A few points on your code:
public interface UserRepository extends JpaRepository<User, String> {
Query("SELECT id fName, lastName, email FROM User u WHERE u.fName=(:fName)")
public List<User> findEmails(#Param("fName") String fName);
}
You are creating the repository with JpaRepository<User, String> but in your class User id is not of type String. Consider making the ID field a String.
The method is findEmails but it is returning a List<User>? I would expect such a function to return List<String> - each string of course being an email. You may consider renaming this function to avoid future confusion. Not a big deal though.
In your query:
#Query("SELECT id fName, lastName, email FROM User u WHERE u.fName=(:fName)")
You should change this to:
#Query("SELECT u.id u.fName, u.lastName, u.email FROM User u WHERE u.fName=(:fName)")
That should fix the serialization issue you are having.

I very much agree with the suggestion of Fermi-4. Using a better naming convention is key to have a better easy manageable code. You should follow them. To solve your problem, just do the following and it will solve your problem.
public interface UserRepository extends JpaRepository<User, long> {
public List<User> findByFname(String fName);
}
Also, consider changing USER Entity definition to add serializable implementation as below
#Component
#Entity
#Table(name = "USERDB.USERS")
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 3L;

Create additional User constructor with only few arguments that you wish to fetch from table
public User(long id, String fName, String lastName, String email) {
this.id = id;
this.fName = fName;
this.lastName = lastName;
this.email = email;
}
Adjust JP query like following
#Query("SELECT new com.package.User(u.id, u.fName, u.lastName, u.email) FROM User u WHERE u.fName = :fName")
public List<User> findByEmail(#Param("fName") String fName);
replace com.package with your User's actual package

Related

Insert into Multiple tables using JPA #Query

Insert JSON values into multiple tables using JPA and spring-boot.
User Table
#Entity
class User {
private #Id #GeneratedValue Long id;
private String name;
#OneToOne(cascade = {
CascadeType.All
})
#JoinColumn(referencedColumnName = "productid")
private Product product;
public User() {}
public User(String name, Product product) {
this.name = name;
this.product = product;
}
}
Product Table
#Entity
class Product {
private #Id #GeneratedValue Long productid;
private String productName;
public Product() {}
public Product(String productName) {
this.productName = productName;
}
}
Repository
#Repository
public interface UserRepo extends JpaRepository < User, Long > {}
Json Input
{
"name": "John",
"product": {
"productName": "Product 1"
}
}
Rest Controller
UserRepo usrRepo;
#PostMapping("/user")
User addEmployee(#RequestBody User user) {
return usrRepo.save(user);
}
When I use the above, both User and Product tables get updated with the new values from JSON. But I want to have the same functionality using #Query. Using the below code, I can update one table but not both.
Help me to insert JSON values into multiple tables using #Query. I am using cockroach db, please suggest if there is any other way to achieve this instead of spring-data-JPA.
Query
#Modifying
#Transactional
#Query(value = "insert into user (name, productid) values (:#{#user.name}, :#{#user.productid})", nativeQuery = true)
void insert(#Param("user) User user);

Spring hibernate ignore json object

I need to remove cart object from json, but only in one controller method and that is:
#GetMapping("/users")
public List<User> getUsers() {
return userRepository.findAll();
}
User
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotBlank(message = "Name cannot be empty")
private String name;
#OneToOne
private Cart cart;
}
Cart
#Entity
public class Cart {
#Id
private String id = UUID.randomUUID().toString();
#OneToMany
private List<CartItem> cartItems = new ArrayList<>();
#OneToOne
#JsonIgnore
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
}
I have done it with simple solution so i loop trough all users, and set their cart to null,and then anotated user entity with #JsonInclude(JsonInclude.Include.NON_NULL)
But i dont think this is propper solution, so im searching for some better solution..
How am i able to do this?
Thanks...
You can create DTO (data transfer object) class like this:
#Data
public class UsersDto {
private Integer id;
private String name;
public UsersDto(User user) {
this.id = user.id;
this.name= user.name;
}
}
and than create List<UsersDto>
#GetMapping("/users")
public List<UsersDto> getUsers() {
List<User> users = userRepository.findAll();
return users
.stream()
.map(o -> new UsersDto(o))
.collect(Collectors.toList());
}
You should use Data Projection.
In your use case, you can use an interface projection:
public interface CartlessUser {
Integer getId();
String getName();
}
And In your repository:
public interface UserRepository extends JpaRepository<User, Integer> {
List<CartlessUser> findAllBy();
}
The interface projection will help generate the sql query for only selecting the id, name fields. This will save you from fetching the Cart data when you're just going to throw it away anyways.

Hibernate: How to display data from multiple table

I am new in spring/hibernate technologies, I have tried to find an information about it, but failed, so if you can help I will be so thankful!
I need to display a JSON response in browser of multiple tables, one of the table has primary key for another one.
My entities:
#Entity
#Table
#ToString
public class Book {
#Id
#GeneratedValue(strategy = AUTO)
#JsonView(Views.IdName.class)
private Long book_id;
#JsonView(Views.IdName.class)
private String name;
#Column(length = 1000000)
private String text;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="author_id")
#JsonView(Views.IdName.class)
private Author author;
// ....get/set methods
Another one:
#Entity
#Table
#ToString
public class Page {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
#Column(length = 1000000)
private String text;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "book_id")
private Book book;
// ...get/set methods
My controllers:
#RestController
#RequestMapping("books")
public class BookController {
private final BookRepo bookRepo;
#Autowired
public BookController(BookRepo bookRepo) {
this.bookRepo = bookRepo;
}
#GetMapping
#JsonView(Views.IdName.class)
public List<Book> getAll() {
return bookRepo.findAll();
}
#GetMapping("{id}")
public Book getOne(#PathVariable("id") Book book) {
return book;
}
}
Another one:
#RestController
#RequestMapping("authors")
public class AuthorController {
private final AuthorRepo authorRepo;
#Autowired
public AuthorController(AuthorRepo authorRepo) {
this.authorRepo = authorRepo;
}
#GetMapping
public List<Author> getAll() {
return authorRepo.findAll();
}
#GetMapping("{id}")
public Optional<Author> getOne(#PathVariable("id") Long id) {
return authorRepo.findById(id);
}
}
And also repo for interaction with DB (they are the similar):
public interface AuthorRepo extends JpaRepository<Author, Long> {
}
So when I make a request for get all books, I take the following JSON:
enter image description here
Bit I want different result, something like:
[
{
"book_id" : 1,
"name": "name 1 book",
"author" :
{
"author_id" : 1,
"name": "some_name"
}
}
]
Also, when I tried to make a request for /authors/1, I will get the following response (something like recursion) :
enter image description here
So any help how can I handle with it? Thank you!
You can use a #NoRepositoryBean
like in this example:
#NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
extends Repository<T, Long> {
#Query("select new com.example.YourObjectWithConstructor(e.attribute, sub.sub_attribute) from entity e inner join e.subtable sub where e.attribute = ?1")
List<YourObjectWithConstructor> findAllByAttribute(String attribute);
}
My example may not be 100% correct, I did not check the syntax. Feel free to explore it
Check this also:
JPQL Create new Object In Select Statement - avoid or embrace?

OneToOne ConstraintViolation while saving a new Record, PK Provided

We have an Entity called Customers that has a OneToOne relationship to the Entity Address.
The Customer's PK should be manually defined. The Address' PK should be automatically defined.
So, in Customer I omitted the #GeneratedValue and I'm providing is value manually. But, when trying to save I'm getting the following error:
2018-11-07 10:42:17.810 ERROR 1257 --- [nio-8080-exec-2] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [Validation failed for classes [br.com.customers.entity.Address] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='não pode ser nulo', propertyPath=street, rootBeanClass=class br.com.customers.entity.Address, messageTemplate='{javax.validation.constraints.NotNull.message}'}
The problem is that the address.street is being provided and I can't realize why JPA is complaining that it's null...
Here are the JSON body that I'm trying to save. (It's being deserialized correctly, as, Address is not NULL)
{
"customer_Id": 50,
"name": "name",
"company_name": "company_name",
"email": "email#provider.com",
"business_phone": "(00) 1111-2222",
"mobile_phone": "(00) 1111-2222",
"document": "123456789",
"state_registration_number": "ISENTO",
"state_registration_type": "NO_CONTRIBUTOR",
"city_registration_number": "ISENTO",
"classification": "AUTO",
"address": {
"street": "STREET NAME",
"number": "NUMBER",
"complement": "COMPLEMENT",
"zip_code": "ZIP_CODE",
"neighborhood": "NEIGHBORHOOD",
"city": "CITY",
"state": "STATE"
}
}
Here are the Customer Entity:
#Data
#Entity(name = "X_CUSTOMERS")
public class Customer {
#Id
private int customer_Id;
#NotNull
private String name;
private String company_name;
private String email;
private String business_phone;
private String mobile_phone;
#NotNull
private String document;
private String state_registration_number;
private String state_registration_type;
private String city_registration_number;
#NotNull
private String classification;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinColumn(name = "address_id")
private Address address;
}
And here, Address Entity:
#Data
#Entity(name = "X_ADDRESS")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int address_Id;
#NotNull
private String street;
private String number;
private String complement;
private String zip_code;
private String neighborhood;
private String city;
private String state;
}
What Am I doing wrong?
Thanks!!!
Adding the code do persist the entities:
Customer Repository:
public interface CustomerRepository extends JpaRepository<Customer, Integer> {
}
To persist:
#RestController
#RequestMapping("/customers")
public class CustomersController {
private CustomerRepository customerRepository;
public CustomersController(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#PostMapping
public Customer postCustomer(#RequestBody Customer customer) {
return customerRepository.save(customer);
}
}
From reading the Hibernate documentation, the save operation only persist entities with auto generated ids. So, if you intend to set the id yourself, then what you need, is to change your insert method for persist. And since you customer has an id that is not auto generated, maybe this could be the issue. You can read more in this blog.
#PostMapping
public Customer postCustomer(#RequestBody Customer customer) {
return customerRepository.persist(customer);
}
Hope it helps.
If you add CascadeType.MERGE, it will work
#OneToOne(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "address_id")
private Address address;
you set the customer id(50) so the following line of SimpleJpaRepository will be executed.
return this.em.merge(entity);

Retrieve an object which has a POJO as a primary key with Spring JPA

I have the following classes: DepartmentMember and Account, mapped by a OneToOne relationship.
This is the DepartmentMember class:
#Entity(name="departmentmember")
#Table(name="departmentmember")
#Embeddable
public class DepartmentMember {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="name", nullable=false)
private String nume;
#Column(name="lastName", nullable=false)
private String prenume;
#OneToOne(mappedBy="departmentMember",cascade=CascadeType.ALL,fetch=FetchType.LAZY, optional=false)
#JsonIgnore
private Account account;
public DepartmentMember() {}
public DepartmentMember(String nume, String prenume, String cNP, String email) {
super();
this.nume = nume;
this.prenume = prenume;
}
//getters and setters
}
And this is the Account class :
#Entity(name="users")
#Table(name="users")
public class Account {
#Id
private int id;
#Column(name="username", unique=true, nullable=false)
private String username;
#Column(name="password", nullable = false)
private String password;
#Column(name="authorities", nullable=false)
private String authorities;
#OneToOne(fetch=FetchType.EAGER)
#MapsId
#Embedded
private DepartmentMember departmentMember;
public Account() {}
public Account(String username, String password, String authorities) {
super();
this.username = username;
this.password = password;
this.authorities = authorities;
}
//getters and setters
}
I have defined an interface AccountRepository which extends the CrudRepository interface provided by Spring JPA.
What I want to do is define a query, which takes as a parameter a DepartmentMember id and retrieves the associated account for that member. Now this is how an Account object looks like:
{
"username": "Maria_Popescu",
"password": "4ec38c6e-2463-4562-99ba-9f6c2b4528c4",
"authorities": "ROLE_USER",
"departamentMember": {
"id": 2,
"nume": "Popescu",
"prenume": "Maria",
}
I tried using the findOne(int id) method, but it didn't work, so which is the correct approach to solve this?
Edit:
In the AccountRepository I have defined the following method :
Account findByDepartmentMemberId(int id) and I still get a not found error.
There was actually another problem in my controller. I managed to get it working by adding
Account findByDepartmentMemberId(#Param("id")int id);
in the AccountRepository

Resources