Spring Autowired Repository in Abstract Class - java

I'm trying to create an abstract class that performs the common REST operations that are required, but can't work out if what I'm trying to do is possible. I've tried a number of approaches, but have stripped the code below right back to how it should work in my head
Classes updated as per suggestions below. Problem now is that the constructor in the concrete class isn't valid, as CustomerRepository isn't assignable to JpaRepository, though it extends that interface.
AbstractRestController
public abstract class AbstractRestController<T> {
private final JpaRepository<T, Serializable> repository;
public AbstractRestController(JpaRepository<T, Serializable> repository) {
this.repository = repository;
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<JsonResponseBody<T>> getOne(#PathVariable Long id) {
T restObj = repository.findOne(id);
JsonResponseBody<T> response = new JsonResponseBody<>(ResponseStatus.SUCCESS, restObj);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(response);
}
protected JpaRepository<T, Serializable> getRepository() {
return repository;
}
}
CustomerController
#RestController
#RequestMapping(value = "/api/v1/customer")
public class CustomerController extends AbstractRestController<Customer> {
#Autowired
public CustomerController(CustomerRepository repository){
super(repository);
}
}
CustomerRepository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}

Indeed, as #dino-tw mentions, you are trying to instantiate an abstract class with an undefined dependency. You can absolutely have an abstract controller class, and even define request handling methods that will be inherited by all subclasses. Try this instead:
public abstract class AbstractRestController<T, ID extends Serializable> {
private final JpaRepository<T, ID> repository;
public AbstractRestController(JpaRepository<T, ID> repository){
this.repository = repository;
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<JsonResponseBody<T>> getOne(#PathVariable ID id) {
T restObj = repository.findOne(id);
JsonResponseBody<T> response = new JsonResponseBody<>(ResponseStatus.SUCCESS, restObj);
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(response);
}
protected JpaRepository<T, ID> getRepository(){ return repository; }
}
#RestController
#RequestMapping(value = "/api/v1/customer")
public class CustomerController extends AbstractRestController<Customer, Long> {
#Autowired
public CustomerController(CustomerRepository repository){
super(repository);
}
}

Related

java.lang.classcastexception- spring, rest

My class Rest doesn't invoke my other class
#CrossOrigin(allowedHeaders = "*")
#RestController
public class PhoneRest {
#Autowired
private TradePhoneService service;
#CrossOrigin(origins = {"http://localhost:4200","http://localhost:8081"})
#PostMapping(value = "/listarRamaisDiponiveis", produces = "application/json")
public ResponseEntity <List<TpbPrvtRecPhoneExtVO>> listarRamaisDiponiveis() {
List<TpbPrvtRecPhoneExtVO> list = new ArrayList<>();
try {
list =service.listarRamaisDiponiveis(); // here java.lang.classcastexception
About interface TradePhoneService:
public interface TradePhoneService {
public List<TpbPrvtRecPhoneExtVO> listarRamaisDiponiveis();
About class TpbPrvtRecPhoneExtVO:
public class TpbPrvtRecPhoneExtVO extends BaseVO implements Serializable{
And about class TradePhoneServiceImpl that implements TradePhoneService:
#Transactional(rollbackOn=Exception.class)
#Service
public class TradePhoneServiceImpl implements TradePhoneService {
#Autowired
private TradePhoneDAO dao;
public List<TpbPrvtRecPhoneExtVO> listarRamaisDiponiveis() {
return listBeansToVos(dao.listarRamaisDiponiveis());
}
Where is the problem?
Thanks
Marcos Vizine

how inject implementation of JpaRepository

I want to use method a from UserRepository in UserService, but I'm getting jpaRepository instead my custom implementation, how should I write classes to get it?
Repository:
#Repository
public interface UserRepository<UserEntity extends EntityInterface,Long> extends JpaRepository<UserEntity,Long> {
Optional<UserEntity> findUserByLogin(String login);
}
CrudAbstractService with generics method:
public abstract class CrudAbstractService<ENTITY extends EntityInterface, DTO extends DTOInterface> {
protected final JpaRepository<ENTITY, Long> jpaRepository;
protected final Validator<DTO> validator;
protected final MapperInterface<ENTITY, DTO> mapper;
private Class<ENTITY> entityClazz;
public CrudAbstractService(JpaRepository<ENTITY, Long> jpaRepository,
Validator<DTO> validator, MapperInterface<ENTITY, DTO> mapper) {
this.jpaRepository = jpaRepository;
this.validator = validator;
this.mapper = mapper;
}
public Iterable<DTO> findAll() {
List<ENTITY> allEntities = jpaRepository.findAll();
if (allEntities == null) {
throw new EntityNotFound(entityClazz);
}
List<DTO> mappedDTOs = mapper.toDTOs(allEntities);
return mappedDTOs;
}
public void delete(DTO dto) {
validator.validate(dto);
ENTITY entity = mapper.toEntity(dto);
jpaRepository.delete(entity);
}
public DTO save(DTO dto) {
validator.validate(dto);
ENTITY entity = mapper.toEntity(dto);
ENTITY save = jpaRepository.save(entity);
if (save == null) {
throw new EntityNotFound(entityClazz);
}
DTO mappedDTO = mapper.toDTO(save);
return mappedDTO;
}
}
Implementation of CrudUserService, there I want to inject UserRepository instead of JpaRepository:
#Service
public class UserService extends CrudAbstractService<UserEntity,UserDTO> {
private MapperInterface<LectureEntity,LectureDTO> lectureMapper;
public UserService(UserRepository<UserEntity, Long> jpaRepository,
Validator<UserDTO> validator, MapperInterface<UserEntity, UserDTO> mapper,
MapperInterface<LectureEntity,LectureDTO> lectureMapper) {
super(jpaRepository, validator, mapper);
this.lectureMapper = lectureMapper;
}
public UserDTO findUserByLogin(String login) {
if (login == null) {
throw new UserNotFoundException();
}
//Here i want use UserRepository method instead of JpaRepository.
Optional<UserEntity> userByLogin = jpaRepository.findUserByLogin(login);
UserEntity userEntity = userByLogin.orElseThrow(UserNotFoundException::new);
List<LectureEntity> reservations = userEntity.getReservations();
List<LectureDTO> lectureDTOS = lectureMapper.toDTOs(reservations);
UserDTO userDTO = mapper.toDTO(userEntity);
userDTO.setLectures(lectureDTOS);
return userDTO;
}
}
I think you don't need to make you repository interface generic.
So, replace this:
#Repository
public interface UserRepository<UserEntity extends EntityInterface,Long> extends JpaRepository<UserEntity,Long> {
Optional<UserEntity> findUserByLogin(String login);
}
with this:
#Repository
public interface UserRepository extends JpaRepository<UserEntity,Long> {
Optional<UserEntity> findUserByLogin(String login);
}
And use it in your service:
#Service
public class UserService extends CrudAbstractService<UserEntity,UserDTO> {
private MapperInterface<LectureEntity,LectureDTO> lectureMapper;
public UserService(UserRepository jpaRepository,
Validator<UserDTO> validator, MapperInterface<UserEntity, UserDTO> mapper,
MapperInterface<LectureEntity,LectureDTO> lectureMapper) {
super(jpaRepository, validator, mapper);
this.lectureMapper = lectureMapper;
}
}
If you need to map your entities to DTOs then you can try to use JPA projections
Regarding throwing an exception in findAll() - in my opinion, it's not a good idea. You should probably return just empty list and let the clients of your class decide what to do in case of missing entities.
Also in your case I would try to avoid using abstract classes and inheritance and use composition instead.
Inheritance versus composition: How to choose and Why should I prefer composition over inheritance?

#Autowired abstract class from subclass

I have a controller for REST services for a particular type of resources (Symptoms) that looks like this:
#RequestMapping(value = "/symptom", produces = "application/json")
public class SymtomController {
#Autowired
private SymptomRepository repository;
#Autowired
private SymptomResourceAssembler assembler;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Collection<SymptomResource>> findAllSymptoms() {
List<SymptomEntity> symptoms = repository.findAll();
return new ResponseEntity<>(assembler.toResourceCollection(symptoms), HttpStatus.OK);
}
...
}
But, as I need to produce more controllers, for other resources, I would like to generate an abstract class and subclasses
public class AbstractController<Entity extends AbstractEntity, Resource extends GenericResource {
// repository and assembler somehow transferred from subclasses
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Collection<Resource>> findAllResources() {
List<Entity> entities = repository.findAll();
return new ResponseEntity<>(assembler.toResourceCollection(entities), HttpStatus.OK);
}
...
}
#RequestMapping(value = "/symptom", produces = "application/json")
public class SymtomController extends ResourceController<SymptomEntity, SymptomResource>{
#Autowired
private SymptomRepository repository;
#Autowired
private SymptomResourceAssembler assembler;
...
}
But I do not know it is possible, somehow, to transfer the autowired elements in the subclasses to the abstract class in a nice way (i.e. not sending them as parameters on each function call).
Any ideas?
Move the dependencies to the parent.
abstract class Parent {
#Autowired
protected MyRepository repo;
#PostConstruct
public void initialize(){
System.out.println("Parent init");
Assert.notNull(repo, "repo must not be null");
}
}
#Component
class Child extends Parent {
#PostConstruct
public void init(){
System.out.println("Child init");
Assert.notNull(repo, "repo must not be null");
}
}

Override default save Entity on Spring Data Rest

I would like to override the default CrudRepository save method that is also exported to Rest api:
#RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long> {
#Override
#RestResource(exported=false)
User save(User user);
}
In my ApiController I have set up a requestmapping like this:
#RequestMapping(value = "/", produces = "application/json", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Resource<User>> registerUser(
#RequestParam("name") String name,
#RequestParam("alias") String alias,
#RequestParam("email") String email,
#RequestParam("password") String password,
#RequestParam("dateOfBirth") String dateOfBirth,
#RequestParam("imageIdentifier") String imageIdentifier) {
User user = new User();
//try {
// userReposiotry.save(user);
//} catch (Exception e) {
//}
Resource<User> resource = toResource(user);
return new ResponseEntity<Resource<User>>(resource, HttpStatus.OK);
}
The problem is when I try to POST to localhost:8080/api/users it returns a "Method Not allowed" which is good because it was set "exported=false"
But how can I implement my own POST for localhost:8080/api/users ?
Thanks
Another way to do it is to create a custom repository implementation like so:
#RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
#Override
#RestResource(exported=false)
User save(User user);
}
public interface UserRepositoryCustom {
<S extends User> S save(T entity);
}
public UserRepositoryImpl implements UserRepositoryCustom {
<S extends User> S save(T entity) {
// implementation code...
}
}
If you look at the CrudRepository you will find a method <S extends T> S save(S entity);, that's where I got the save(..) from, just changed the extends T to extends User.
The other thing that I would pay attention to is the naming of the classes/interfaces, try to be consistent. The way I named them should work for you, the UserRepositoryImpl must have that name in order for this to work.
Doing this you won't have to set exported=false and you can just use the save() method as you would do normal.
Found a solution:
#BasePathAwareController
#RequestMapping("/users")
public class RestApiController implements ResourceProcessor<Resource<User>>{
#Autowired
private EntityLinks entityLinks;
#RequestMapping(method=RequestMethod.POST)
#ResponseBody
public ResponseEntity<Resource<User>> saveUser(#Param("name") String name) {
// Testing
System.out.println(name);
Resource<User> resource = new Resource<>(new User());
return new ResponseEntity<>(resource , HttpStatus.OK);
}
#Override
public Resource<User> process(Resource<User> resource) {
LinkBuilder lb = entityLinks.linkFor(User.class);
resource.add(new Link(lb.toString()));
return resource;
}
}
The CrudRepository save is still set as exported=false as in my question.

Writing a new dao. No Hibernate Session bound to thread

So I have such classes
BaseDao
public interface BaseDao<T> {
T save(T entity);
}
UserDao
public interface UserDao extends BaseDao<User> {
User getUserByUserName(String name);
}
GenericDao
public abstract class GenericDao implements BaseDao {
#Autowired
protected SessionFactory sessionFactory;
private Class<?> getEntityClass() {
return ((Class) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]);
}
public T save(T entity) {
Integer id = (Integer) sessionFactory.getCurrentSession().save(entity);
return (T) sessionFactory.getCurrentSession().get(getEntityClass(), id);
}
}
UserDaoImpl
#Repository
public class PgUserDaoImpl extends GenericDao<User> implements UserDao {
public User getUserByUserName(String name) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
User.class);
criteria.add(Restrictions.eq("name", name));
return (User) criteria.uniqueResult();
}
}
GenericService
public class GenericService<T extends GenericDao<E>, E> {
protected T dao;
public GenericService(T dao) {
setDao(dao);
}
#Transactional
public E save(E entity) {
return dao.save(entity);
}
}
UserServiceImpl
#Service("userServiceImpl")
public class UserServiceImpl extends GenericService<PgUserDaoImpl, User>
implements UserService {
#Autowired
public UserServiceImpl(PgUserDaoImpl dao) {
super(dao);
}
#Autowired
private UserAssebmler assebmler;
#Transactional
#Override
public UserDetails loadUserByUsername(String name)
throws UsernameNotFoundException {
.....
}
}
So I write test contoller to save user, but I always get Exception
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
The contoller
#
Controller
#RequestMapping(value = "/test")
public class TestController {
#Autowired
private UserServiceImpl userService;
#RequestMapping(value = "save", method = RequestMethod.GET)
public String test() {
User user = new User();
user.setName("admin");
user.setPassword("21232f297a57a5a743894a0e4a801fc3");
user.setRole(UserRole.ADMIN);
userService.save(user);
return "home";
}
}
Anybody know hot to fix that? Thanks :)
I've solved my question by extending GenericDao class from HibernateDaoSupport class.

Categories

Resources