Spring-Boot subclassing User and findUserByUsername - java

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)!

Related

I get an error when I make my own spring boot query

My problem is when I try to make a customized query, I want to use crud or jpa repositories for my repository to make custom methods to do the following operations on the database, but I can't succeed in any way...
CartRepository
package com.example.registrationlogindemo.repository;
import com.example.registrationlogindemo.model.Cart;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
public interface CartRepository extends CrudRepository<Cart,Long> {
#Query(name = "INSERT INTO cart VALUES (:productID,:userID)",nativeQuery = true)
List<Cart> save(#Param("productID") Long productId, #Param("userID") Long userID);
#Query(name = "SELECT * FROM cart WHERE user_id = ?2",nativeQuery = true)
List<Cart>showAll(Long productID, Long userID);
}
Cart
package com.example.registrationlogindemo.model;
import jakarta.persistence.*;
import lombok.*;
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "cart")
public class Cart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
Controller
#Controller
public class CartController {
private static SessionFactory factory;
Long saveId;
#Autowired
ProductRepository productRepository;
#Autowired
UserRepository userRepository;
CartRepository cartRepository;
#GetMapping("/shoppingCart")
#ResponseBody
public String showCart(Model model, Principal principal){
String nume = principal.getName();
cartRepository.save(saveId,userRepository.AiciVoiAveaId(nume));
Optional<Product> product = productRepository.findById(saveId);
model.addAttribute("product", product.get());
return "shoppingCart";
}
#PostMapping("/shoppingCart/{id}")
public String shoppingCart(#PathVariable Long id){
saveId = id;
return "redirect:/shoppingCart";
}
}
Error
2023-01-05T15:39:16.146+02:00 WARN 16652 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cartRepository' defined in com.example.registrationlogindemo.repository.CartRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Could not create query for public abstract java.util.List com.example.registrationlogindemo.repository.CartRepository.save(java.lang.Long,java.lang.Long); Reason: Failed to create query for method public abstract java.util.List com.example.registrationlogindemo.repository.CartRepository.save(java.lang.Long,java.lang.Long); No property 'save' found for type 'Cart'
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cartRepository' defined in com.example.registrationlogindemo.repository.CartRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Could not create query for public abstract java.util.List com.example.registrationlogindemo.repository.CartRepository.save(java.lang.Long,java.lang.Long); Reason: Failed to create query for method public abstract java.util.List com.example.registrationlogindemo.repository.CartRepository.save(java.lang.Long,java.lang.Long); No property 'save' found for type 'Cart'
Can someone help me?
Hi, I have a problem too, I try to create a specific query for several actions and I get errors and I don't understand why...
Salut,
You can try with JpaRepository:
#Repository
public interface CartRepository extends JpaRepository<Cart, Long> {
#Query("FROM Cart WHERE user.id = :userID")
List<Cart> findAllByUserId(Long userID);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "cart")
public class Cart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_user", nullable = false)
private User user;
}
And then you can use the repository like so:
Cart cartToBeSaved = new Cart(userId);
cartRepository.save(cartToBeSaved); //cart id is auto-generated
Also, I would avoid declaring a global Long saveId;. It has no useful purpose and can lead to more serious issues when multiple users are accessing these endpoints.
I would also move some logic in a service class and I would encapsulate the repositories.
For example:
#Controller
#RequiredArgsConstructor
public class CartController {
private final CartService cartService;
#GetMapping("/shoppingCart")
public String showCart(Model model, Principal principal) {
Product product = cartService.saveProduct(...);
model.addAttribute("product", product.get());
return "shoppingCart";
}
#PostMapping("/shoppingCart/{id}")
public String shoppingCart(#PathVariable Long id){
//something here
}
}
#Service
#RequiredArgsConstructor
public class CartService {
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final CartRepository cartRepository;
public Product saveProduct(...) {
//some business logic here
}
}
Your save() method contains a faulty sql command. The INSERT command for SQL does not work this way. I don't know exactly the qualities of your Cart object, but it needs to look like the following way.
#Modifying
#Query(name = "INSERT INTO cart (productId, userId) VALUES (:productID,:userID)",nativeQuery = true)
Cart save(#Param("productID") Long productId, #Param("userID") Long userID);
A SQL INSERT command must contain the column names of the corresponding table.
I also recommend using #Modifying annotation for commands such as INSERT, UPDATE, DELETE. You can find detailed information about #Modifying anatotion here.

Why does STS warn me about non matching parameters?

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.

Error updating Spring-data-jpa on MappedSuperclass

I got an error on startup upgrading from version 1.4.3 to 1.5+ or 1.6.0;
I am using Hibernate 4.3.5
The exception is:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'IAccountRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: This class [class com.model.entities.BaseEntity] does not define an IdClass
and the entities:
#MappedSuperclass
#EntityListeners(EntityAuditListener.class)
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Audited
#Basic(optional = false)
#Column(name = "isactive", nullable = false, columnDefinition = "BOOLEAN DEFAULT TRUE")
private boolean isActive = true;
protected BaseEntity() {
}
protected BaseEntity(boolean isActive) {
this.isActive = isActive;
}
........... more attributes and getters and setters
}
#Entity
#Table(name = "accounts", schema = "public")
#SequenceGenerator(name = "seq_account", sequenceName = "seq_account", initialValue = 1, allocationSize = 1)
#Audited
public class Account extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_account")
#Column(name = "accountid")
private Long accountId;
---- more attributes and getters and setters
}
To me it looks like Spring-data-jpa checks hierarchy in the same way Hibernate does but considering the super class as an entity.
Do you know if it's my error or a bug and any workaround?
Many thanks.
EDIT
MY repositories are as follow:
#Transactional(propagation = Propagation.MANDATORY)
#NoRepositoryBean
public interface IBaseRepository<T extends BaseEntity, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
public Page<T> findByIsActiveTrue(Pageable pageable);
public List<T> findByIsActiveTrue();
}
#Transactional(propagation = Propagation.MANDATORY)
public interface IAccountRepository extends IBaseRepository<Account, Long> {
-- mix of queries by method name like:
public Account findByAccountIdAndIsActiveTrue(Long accountId);
-- and #Query like:
#Query(value = "SELECT COALESCE(SUM(a.accountCreditLimit), 0) FROM Account a WHERE a.name = :name")
public BigDecimal readAccountCreditLimits(#Param("name") String accountName);
}
------ and many more repositories as above
I ran into exactly the the same problem upgrading from 1.4.3 with hibernate 4.3.5. My repository interfaces extend PagingAndSortingRepository. I found the below line in Spring data change log for 1.6.0:
Can't use pagination (Pageable) with #IdClass entities (spring-data-jpa 1.4.3 & Hibernate 4.1.9). (DATAJPA-472)
(source: http://docs.spring.io/spring-data/jpa/docs/1.6.0.RELEASE/changelog.txt)
Replaced PagingAndSortingRepository with JpaRespository and seems to work fine.
After some research and many attempts, I found out the error was on my repository hierarchy.
So after improving my generics now it works as before.
From:
#Transactional(propagation = Propagation.MANDATORY)
public interface IBaseAddressRepository<T extends BaseEntity> extends IBaseRepository<T, Long> {
}
To:
#Transactional(propagation = Propagation.MANDATORY)
public interface IBaseAddressRepository<T extends Address> extends IBaseRepository<T, Long> {
}
Does anyone know if this is the expected behaviour? Although it's much better to narrow generic types as much as possible, my old repository should work too as long as Address extends BaseRepository, shouldn't it?
Spring Data tries to create an Implementation for your BaseRepository.
This requires an full Entity with #Entity, #Id.
It is possible to tell Spring that you don't need an Implemenation for the BaseRepository by adding the Annotation #NoRepositoryBean
This is how my "BaseRepository" looks like:
import java.io.Serializable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
#NoRepositoryBean
public interface IsActiveAwareRepository<T extends AbstractEntity, ID extends Serializable> extends CrudRepository<T, ID>{
public Iterable<T> findByIsActive(Boolean isActive);
}

EJB webservice, entity is not mapped error

I have entity class User
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(unique = true, nullable = false)
private String email;
/** skipped **/
And stateless EJB DAO class(I am using jpa/hibernate) with method
#Override
public User findByEmail(String name) {
TypedQuery<User> query = entityManager.createQuery("from User where email= :email", User.class);
query.setParameter("email", name);
return query.getSingleResult();
}
When I execute this method from page(using jsf), it works fine. The problem occurse when I execute this method from WebService EJB bean. I've got following exception
org.hibernate.hql.internal.ast.QuerySyntaxException: User is not mapped [from User where email= :email]
My web service is very simple:
#Stateless(name = "webServiceBean")
#WebService(serviceName = "webService")
public class MyWebService{
#EJB
private UserDao dao;
public String findUser(String email) {
return dao.findByEmail(email);
}
}
Application is deployed on JBoss7. I am using EJB 3.1 and hibernate 4.3
Does anybody knows what is the reason of this exception? And why it occurs only in WebService?
Make sure User, along with the package name, is listed in hibernate config file.
Either use just #Entity or #Entity(name = "User"). You have named your entity as user but in your query trying to refer to it as User.
Check the docs for the #Entity's name attribute.
public abstract java.lang.String name
(Optional) The entity name. Defaults to the unqualified name of the entity class. This name is used to refer to the entity in queries. The name must not be a reserved literal in the Java Persistence query language.

NucleusUserException: Cannot access field on entity extending mapped superclass

I am running into a NucleusUserException while querying my google datastore instance. I am querying for a field that exists on a MappedSuperclass on the class that extends it. Here is my abstract class that contains the field I am interested in:
#Entity
#MappedSuperclass
#JsonIgnoreProperties({ "password" })
public abstract class AbstractUser implements User {
#Persistent
protected String emailAddress;
public void setEmailAddress(String email) {
this.emailAddress = email;
}
public String getEmailAddress() {
return this.emailAddress;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long key;
//Other stuff.
}
The concrete instance looks like this:
#Entity
public class Client extends AbstractUser {
//Things that only clients have.
}
My query that is failing looks like this:
List existingUsersWithEmail = manager
.createQuery(
"SELECT c from Client AS c WHERE c.emailaddress = :mail")
.setParameter("mail", request.getEmailAddress())
.getResultList();
The exception is this:
Cannot access field emailaddress on type org.workouthound.user.Client
org.datanucleus.exceptions.NucleusUserException: Cannot access field emailaddress on type org.workouthound.user.Client
at org.datanucleus.query.compiler.JavaQueryCompiler.getType(JavaQueryCompiler.java:552)
at org.datanucleus.query.compiler.JavaQueryCompiler.getType(JavaQueryCompiler.java:529)
at org.datanucleus.query.symbol.SymbolTable.getType(SymbolTable.java:118)
at org.datanucleus.query.expression.PrimaryExpression.bind(PrimaryExpression.java:118)
at org.datanucleus.query.expression.DyadicExpression.bind(DyadicExpression.java:85)
at org.datanucleus.query.compiler.JavaQueryCompiler.compileFilter(JavaQueryCompiler.java:299)
at org.datanucleus.query.compiler.JPQLCompiler.compile(JPQLCompiler.java:75)
at org.datanucleus.store.query.AbstractJPQLQuery.compileInternal(AbstractJPQLQuery.java:246)
at org.datanucleus.store.query.Query.setImplicitParameter(Query.java:690)
at org.datanucleus.jpa.JPAQuery.setParameter(JPAQuery.java:428)
at org.workouthound.rest.client.UserResources.emailIsRegistered(UserResources.java:55)
at org.workouthound.rest.client.UserResources.createClient(UserResources.java:33)
I am new to DataNucleus and Google Data Store. I attempted to follow the tutorial as outlined here however I very well could have missed something. Please let me know as additional information is necessary.
UPDATE:
If I change the field name to email as well as the getters, setters and query, it works...why?

Categories