Extending existing spring jpa repository functions - java

I'm using Spring to communicate with mysql db.
I've got an Entity class and an interface that extends CrudRepository.
Everything works great - read/write etc.
I want to 'extend' the findAll methods. I want to manipulate the received data from findAll before returning it.
user class:
#Entity
#Table(name = "user")
public class User
{
private String name;
private String age;
private String type;
getters/setters
}
repo:
#Repository
public interface UserRepo extends CrudRepository<User, Long> {
List<User> findAll();
Map<String, String> afterManipulatedFindAllData();
}
I want that afterManipulatedFindAllData() will manipulate findAll data as I like.
Is that possible?
added
after reviewing #BoristheSpider link:
interface UserRepository {
Map<String, String> afterManipulatedFindAllData();
}
class UserRepositoryImpl implements UserRepository{
public Map<String, String> afterManipulatedFindAllData() {
////how this method receive the 'findAll' data?//////
}
}
public interface UserRepo extends CrudRepository<User, Long>, UserRepository
{
List<User> findAll();
Map<String, String> afterManipulatedFindAllData();
}
Thanks a lot,
Avi

You can always define new methods in the repository by specifying a JPA query for them:
#Component
public interface UsersRepository extends JpaRepository<User, UUID> {
public List<User> findByEntityStatus(EntityStatus status);
#Query("from User user left outer join fetch user.areas where user.code = :code")
public User findByCode(#Param("code") String code);
}
Maybe this could work for you?

Related

Is it possible to register with Container without using #Repository or #Bean?

public interface AccountRepository extends CrudRepository<AccountDBModel, Long> {
#Modifying
#Query(value = PortfolioQuery.ACCOUNT_INSERT)
void insert(#Param("exchangeId") Long exchangeId, #Param("name") String name, #Param("siteAccount") String siteAccount,
#Param("memo") String memo, #Param("createdAt") Long createdAt, #Param("updatedAt") Long updatedAt,
#Param("isActive") Boolean isActive);
#Modifying
#Query(value = PortfolioQuery.ACCOUNT_UPDATE)
void update(#Param("id") Long id, #Param("exchangeId") Long exchangeId, #Param("name") String name,
#Param("siteAccount") String siteAccount, #Param("memo") String memo, #Param("updatedAt") Long updatedAt,
#Param("isActive") Boolean isActive);
#Query
Optional<AccountDBModel> findByName(#Param("name") String name);
}
#Service
public class AccountService {
private final AccountRepository repository;
#Autowired
public AccountService(AccountRepository repository) {
this.repository = repository;
}
public void postAccount(AccountBaseModel baseModel) throws Exception {
Long now = System.currentTimeMillis();
this.repository.insert(baseModel.getExchangeId(), baseModel.getName(), baseModel.getSiteAccount(),
baseModel.getMemo(), now, now, baseModel.getIsActive());
}
}
#SpringBootTest
class WaveBackofficeApiApplicationTests {
#Autowired
private ApplicationContext applicationContext;
#Test
public void contextLoads() throws Exception {
if (applicationContext != null) {
String[] beans = applicationContext.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println("bean : " + bean);
}
}
}
}
As you can see in AccountRepository interface I didn't use #Repository in AccountRepository interface.
But why is it registered as a bean in Spring Container?
There are no other class like AppConfig.
The interface itself is not registered as a bean. spring framework provides existing implementation of a repository bean (default impl is the class SimpleJpaRepository), which gets injected based on the specifications you provide in your interface. This specific class has the #Repository annotation and will be picked up by spring as a bean.
A simple overview:
#Repository
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
// code
}
public interface MyRepository extends CrudRepository<T, ID> {}
#Service
public MyService() {
#Autowired private MyRepository myRepository;
}
In the example above, our own repository interface extends CrudRepository, which has an implementation class named SimpleJpaRepository (provided in the framework), and SimpleJpaRepository is registered as a bean. In MyService, we just tell that we want a bean of type MyRepository, and Spring will inject an instance of SimpleJpaRepository.
You created interface called AccountRepository and extended (thus inherited) CrudRepository.
Now just do Ctrl + Left mouse click on CrudRepository, you will end up in it:
#NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAllById(Iterable<? extends ID> ids);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
Intellij actually gives you oportunity to find the implementations of all those methods with arrow down mark on the left side.
So there is a huge class called SimpleJpaRepository that has all the implementations, the actual code.
AND THE THING IS...
SimpleJpaRepository.class does have #Repository in it:
#Repository
#Transactional(
readOnly = true
)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

Jpa Repository save Inferred type 'S' for type parameter 'S' is not within its bound

I have entity user
#Entity
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
private String password;
}
UserRepository
public interface UserRepository extends JpaRepository<UserRepository, Long> {
User findByUsername(String username);
}
Controller
#RestController
#RequestMapping("/user")
public class UserController {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public UserController(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#PostMapping("/sign-up")
public void signUp(#RequestBody User user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
}
In class UserController in signUp method userRepository.save(user) throw error Inferred type 'S' for type parameter 'S' is not within its bound; should implement 'Repository.UserRepository
Your Repository interface declaration isn't correct.
Change
public interface UserRepository extends JpaRepository<UserRepository, Long>
to
public interface UserRepository extends JpaRepository<User, Long>
For me, I got this error because save(list) method in the new version of spring boot does not support a List of objects as a parameter, so I replaced it with saveAll(list) so xRepository.saveAll(list)
The new version is generally 2.0 or higher having this change. They are restricting Save() method for Object. To save List type data we have to use saveAll().
Following is another working solution
return customRepository.findById(id).orElse(null);
Create the save method in the Repository Interface
void save(List<? extends User> user);
Reason: Teacher Liao's spring boot uses 1.4.1.RELEASE, and the new version is generally 2.0 or higher.
Solution: Replace the findOne method
return repo.findById(id).orElse(null);

Mongodb #DBRef query spring

I my spring project, I am using two models with a reference.
A model user which has a reference on a token object.
#Document(collection = "user")
public class User {
#Id
public String id;
#DBRef
public Token token;
}
Then my Token object:
#Document(collection = "token")
public class Token {
#Id
public String id;
public String token;
}
I am trying to query a user from a token.
I tried to create a MongoRepository interface :
#Repository
public interface UserRepository extends MongoRepository<User, String> {
#Query(value="{ 'token.id' : ?0 }")
User findByTokenId(String id);
}
But that's not working. How can I create such kind or request ?
you can use:
#Repository
public interface UserRepository extends MongoRepository<User, String> {
#Query(value="{ 'token.id' : ?0 }")
User findAllByToken_Id(String id);
}

Best way of handling entities inheritance in Spring Data JPA

I've three JPA entity classes A, B and C with the following hierarchy:
A
|
+---+---+
| |
C B
That is:
#Entity
#Inheritance
public abstract class A { /* ... */ }
#Entity
public class B extends A { /* ... */ }
#Entity
public class C extends A { /* ... */ }
Using Spring Data JPA, what is the best way to write repositories classes for such entities?
I know that I can write these:
public interface ARespository extends CrudRepository<A, Long> { }
public interface BRespository extends CrudRepository<B, Long> { }
public interface CRespository extends CrudRepository<C, Long> { }
but if in the class A there is a field name and I add this method in the ARepository:
public A findByName(String name);
I've to write such method also in the other two repositories, and this is a bit annoying.. Is there a better way to handle such situation?
Another point I would like to have is that ARespository should be a read-only repository (i.e. extend the Repository class) while the other two repositories should expose all the CRUD operations.
Let me know possible solutions.
I used the solution also described in this post from Netgloo's blog.
The idea is to create a generic repository class like the following:
#NoRepositoryBean
public interface ABaseRepository<T extends A>
extends CrudRepository<T, Long> {
// All methods in this repository will be available in the ARepository,
// in the BRepository and in the CRepository.
// ...
}
then I can write the three repositories in this way:
#Transactional
public interface ARepository extends ABaseRepository<A> { /* ... */ }
#Transactional
public interface BRepository extends ABaseRepository<B> { /* ... */ }
#Transactional
public interface CRepository extends ABaseRepository<C> { /* ... */ }
Moreover, to obtain a read-only repository for ARepository I can define the ABaseRepository as read-only:
#NoRepositoryBean
public interface ABaseRepository<T>
extends Repository<T, Long> {
T findOne(Long id);
Iterable<T> findAll();
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
and from BRepository extend also the Spring Data JPA's CrudRepository to achieve a read/write repository:
#Transactional
public interface BRepository
extends ABaseRepository<B>, CrudRepository<B, Long>
{ /* ... */ }

Crud methods in service layer using Spring Data JPA

I'm building a small application using Spring and Spring Data JPA and I need to use the CrudRepository methods in the service layer, so I made 2 classes: GenericService and GenericServiceImpl. But I don't know if this is the right or even the best approach.
Here is an example:
POJO:
#Entity
public class User {
#Id
private Long id;
private String username;
}
DAO:
public interface UserDAO extends CrudRepository<User, Long> {
User findOneByUsername(String username);
}
Generic service
public interface GenericService<T, ID extends Serializable> {
<S extends T> S save(S entity);
}
Service
public interface UserService extends GenericService<User, Long> {
User findOneByUsername(String username);
}
Generic service impl.
public class GenericServiceImpl<T, ID extends Serializable> implements GenericService<T, ID> {
#Autowired
private CrudRepository<T, ID> repository;
#Override
public <S extends T> S save(S entity) {
return repository.save(entity);
}
}
Service Impl.
#Service
#Transactional
public class UserServiceImpl extends GenericServiceImpl<User, Long> implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public User findOneByUsername(String username) {
userDAO.findOneByUsername(username);
}
}
Yes, you're providing your own impl that's may do custom things while still reusing the Derived queries from Spring JPA's CrudRepository. I've seen that a lot. :) We need to do that if we want to do our own computation before calling the Derived methods. After all, that computation may very well be part of a repository, so it doesn't make sense to put that logic in the service. But in your scenario, if there are no such custom computations, then this indirection isn't needed. As a commenter mentioned, you should directly use the UserDao interface. :)

Categories