I am using Spring Data Jpa and created a JpaRepository for my User class.
The repository works but Spring Tool Suite gives me a warning for one method.
Following are examples of my domain model classes and the repository:
User:
#Entity
public class User {
#Id
#GeneratedValue
private long id;
private String username;
#ManyToMany
#JoinTable( ... )
private Set<Role> roles = new HashSet<>();
// Getters & setters
}
Role:
#Entity
public class Role {
#Id
#GeneratedValue
private Long id;
private String name;
// Getters & setters
}
UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByRoles(Set<Role> roles);
}
STS marks the method findByRoles() and gives the following message: Parameter type (Set<Role>) does not match domain class property definition (Set).
Why do I get this warning?
change your method name like this
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findBy_Roles(Set<Role> roles);
}
for more details why its complaining see this page heading "2.4.3. Property expressions".
The signature of the method is wrong. It should be List<User> findByRolesIn(Set<Role> roles), because the argument is a collection.
Related
I am trying to fetch customer details from my database using spring boot handler method by incoming get request from postman. I am using birectional #manytomany relationship between Account and Customer entity classes.
Account class -
#Entity
#Table(name = "Account")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int accountNumber;
public String accountType;
public int balance;
#ManyToMany(cascade = CascadeType.ALL)
#JsonManagedReference
#JsonIgnore
private List<Customer> customers;
}
//skipped constructors and getters setters
Customer class
#Entity
#Table(name = "Customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int customerId;
public String firstName;
public String lastName;
public String email;
#ManyToMany(mappedBy = "customers")
#JsonBackReference
private List<Account> accounts;
}
Handler method in controller class -
// fetching all customers
#GetMapping("/customers")
public List<Customer> getallAccounts() {
return (List<Customer>) this.customerServices.getAllAccounts();
}
CustomerServices class -
#Component
public class CustomerServices {
#Autowired
private CustomerRepository customerRepository;
// get all books
public List<Customer> getAllAccounts() {
System.out.println("Fetching all Customers");
return (List<Customer>) this.customerRepository.findAll();
}
}
And CustomerRepository interface -
public interface CustomerRepository extends CrudRepository<Customer, Integer> {
}
When I am sending a get request to fetch all customers from the database
In response it is not showing List of account class objects that are mapped by ManyToMany relationship in Customer class.
I have no idea what is the error.
Somebody please help here.
You specifically annotated accounts field with #JsonBackReference which means:
Linkage is handled such that the property annotated with this annotation is not serialized;
If you don't serialize the accounts field of Customer, what JSON did you expect to get that would list Account objects when returning a list of Customer objects?
Returning a list of Account objects instead, and expecting sub-lists of Customer objects, will also not happen because of the #JsonIgnore annotation, which pretty much have the same serialization effect as #JsonBackReference
There is no error here. The generated JSON is exactly what you asked the system to generate.
I use Micronaut Data with JPA and have two entities. The first one is Recipe:
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToOne
private Category category;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Step> steps;
// + other fields, getters and setters
}
The second one is ParseError which refers to Recipe:
#Entity
#Table(name = "parse_error")
public class ParseError implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Recipe recipe;
#Id
#Enumerated(EnumType.ORDINAL)
#Column(name = "problem_area")
private ProblemArea problemArea;
private String message;
// + other fields, getters and setters
}
Now I would like to provide DTO in API with ParseError properties but not with whole Recipe entity because it contains ManyToOne and OneToMany relations which are not needed in this case. So I created projection DTO for that:
#Introspected
public class ParseErrorDto {
private Integer recipeId;
private String recipeName;
private ParseError.ProblemArea problemArea;
private String message;
// + getters and setters
}
And added listAll() method into ParseErrorRepository:
#Repository
public interface ParseErrorRepository extends CrudRepository<ParseError, Integer> {
List<ParseErrorDto> listAll();
}
But it seems that Micronaut Data is not able to project properties from nested entities or I missed something in the DTO or the repository method:
ParseErrorRepository.java:22: error: Unable to implement Repository
method: ParseErrorRepository.listAll(). Property recipeId is not
present in entity: ParseError
I also tried to create RecipeDto:
#Introspected
public class RecipeDto {
private Integer id;
private String name;
// + getters and setters
}
And updated ParseErrorDto accordingly:
#Introspected
public class ParseErrorDto {
private RecipeDto recipe;
private ParseError.ProblemArea problemArea;
private String message;
// + getters and setters
}
Again no success:
ParseErrorRepository.java:22: error: Unable to implement Repository
method: ParseErrorRepository.listAll(). Property [recipe] of type
[RecipeDto] is not compatible with equivalent property declared in
entity: ParseError
Is Micronaut Data able to handle this use case by DTO projection? If not then is there another way how can I solve it in Micronaut Data?
Now (in latest version 1.0.0.M1) it is not possible. So I created feature request issue for that: https://github.com/micronaut-projects/micronaut-data/issues/184
Current workaround is to map entity bean into DTO bean in Java stream or reactive stream for example and do the properties mapping manually or by Mapstruct.
Update: Here is an answer to question from comments with an example how to do the workaround using Mapstruct:
Add Mapstruct dependency into build.gradle:
implementation "org.mapstruct:mapstruct:$mapstructVersion"
annotationProcessor "org.mapstruct:mapstruct-processor:$mapstructVersion"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:$mapstructVersion"
Define mapper:
import org.mapstruct.Mapper;
#Mapper(
componentModel = "jsr330"
)
public interface ParseErrorMapper {
ParseErrorDto entityToDto(#NotNull ParseError parseError);
EntityReference recipeToDto(#NotNull Recipe recipe);
}
And here is a usage of that mapper in the controller:
#Controller("/parse-error")
public class ParseErrorController {
private final ParseErrorRepository repository;
private final ParseErrorMapper mapper;
public ParseErrorController(ParseErrorRepository repository, ParseErrorMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}
#Get("all")
#Transactional
public Page<ParseErrorDto> getAll(final Pageable pageable) {
return repository.findAll(pageable).map(mapper::entityToDto);
}
}
I'm working on a professional social network application using couchbase server 4.1 with spring data couchbase 2.2.4 in a spring boot application using java 1.8.
I want to replace the next stream which searches a LikeEntity with specific company in the database by a N1QL based query which searches a user with the same previous likeEntity unnested:
Here is the service containing the stream to replace by the query:
#Service
class CompanyServiceImpl implements CompanyService{
#Override
public void addCompanyToLike(UserEntity user, Company company){
int incrementValue=1;
LikeEntity existingLike=user.getLikeEntities()
.stream()
.filter(le->le.getCompany().getName().equals(company.getName()))
.findFirst();
//rest of process
}
}
Here is the different java beans you will need to look at:
UserEntity class:
#Document
#ViewIndexed(designDoc = "user")
class UserEntity implements Serializable{
#Field private String id;
#Reference
private List<LikeEntity> likeEntities=new ArrayList<LikeEntity>();
//Other attributes plus getters and setters:
}
LikeEntity class:
#Document
class LikeEntity implements serializable{
#Reference
private Company company;
#Reference
private Job job;
// other attributes plus getters and setters
}
As you see above, the LikeEntity class may contain any object liked by the user, it can be a company, a job or another object. Also the LikeEntity is stored only inside a user document as element of user's list of likes and not independately in the database.It's my choice of modelization because the LikeEntity won't by used in other java beans of my application
Company:
#Document
#ViewIndexed(designDoc = "company")
class Company implements Serializable{
#Field private String id;
#Field private String name;
//Other attributes plus getters and setters
}
N.B: I've tried the next query inside UserRepository but it didn't work:
UserRepository:
#Repository
#N1qlPrimaryIndexed
#N1qlSecondaryIndexed(indexName = "user")
public interface UserRepository extends CouchbaseRepository<UserEntity, String> {
#Query("SELECT b.*, likeEntity FROM #{#n1ql.bucket} AS b UNNEST b.likeEntities AS likeEntity WHERE b.id=$1 AND likeEntity.company.name=$2")
UserEntity findByLikedCompanyName(String idUser
, String companyName);
}
I'm looking for your answers, and thank you so much in advance.
I have two domain class City and School with OneToMany.
#Entity
public class City {
...
#OneToMany(mappedBy="city")
private Set<School> schools = new HashSet<>();
...
}
#Entity
public class School {
...
#ManyToOne
private City city;
...
}
The corresponding repository are:
#Repository
public interface CityRepository extends JpaRepository<City, Long>{
}
#Repository
public interface SchoolRepository extends JpaRepository<School, Long> {
}
In the CityController's method:
#GetMapping(....)
public ResponseEntity<City> getSchool(#PathVariable Long id) {
City city = cityRepository.findOne(id);
return ResponseUtil.wrapOrNotFound(Optional.ofNullable(city));
}
When I debug to check schools member of the city object, there is an exception:
Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.
Don't know why, I just want to get the city with all schools in that city (but don't want to add some annotation to domain class, I prefer to implement it in #Repository, better using #Query). Appreciated for any help.
In Hibernate, all relationships have FetchType.LAZY by default. You need to set it to eager to get the city.
#ManyToOne(fetch = FetchType.EAGER)
private City city;
I'm aiming for subclassing Spring's User class, as it already has the standard attributes. One question arises as to how am I supposed to implement a CrudRepository that refers to properties in the User class?
Specifically I'm wondering about the most common use case, namely the findUserByUsername (called by UserDetailsService.loadUserByUsername). Here's some code...
#Entity
#SequenceGenerator(name = "generator", initialValue = 1)
public class AppUser extends User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private long id;
public AppUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public long getId() {
return id;
}
}
Attempting to create the CrudRepository normally, will fail miserably with Could not query metamodel upon startup. This makes sense, because the username attribute is not inherited directly to my subclass and User is obviously not marked as #Entity.
#Transactional
#Repository
public interface UserRepository extends CrudRepository<AppUser, Long> {
AppUser findByUsername(String username);
}
Error message upon startup
Error creating bean with name 'userRepository': Invocation of init
method failed; nested exception is java.lang.IllegalArgumentException:
Could not create query metamodel for method public abstract
se.riee.user.AppUser
se.riee.user.UserRepository.findByUsername(java.lang.String)!