Using #Autowired to set Class in Generics - java

I'm trying to setup a generic DAO and service for a project i'm working at, it all seems to be working except I have to manually set the class<T> clazz everywhere I instantiate a repository, which is ok for the ones I have an entire class for (such as the clientRepository one below).
I tried to do the #Autowired setClazz(Class<T> clazz) in the AbstractRepository to solve it but it just doesn't work, when I debug findById(Long id), the clazz is null.
Is there any way to do this or do I really have to call the setClazz everywhere?
AbstractRepository
public abstract class AbstractRepository<T extends IGenericEntity> {
#PersistenceUnit(unitName = "clima_PU")
private EntityManagerFactory entityManagerFactory;
private Session session;
private Class<T> clazz;
#PersistenceContext
protected EntityManager entityManager;
#Autowired
public void setClazz(Class<T> clazz) {
this.clazz = clazz;
}
public T findByID(Long id) {
return (T) entityManager.find(clazz, id);
}
}
GenericRepository
#Repository
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class GenericRepository<T extends AbstractEntity> extends AbstractRepository<T> implements IGenericRepository<T> {
}
ClientRepository
public class ClientRepository extends GenericRepository<ClientEntity> {
/* Custom Queries for Clients */
}

Related

SpringBoot: create object from generic type in generic mapper

I have a lot of entity extend Catalog entity and as well as have a lot of dto that extent CatalogDto
And I have a generic repository, service, and mapper as follows
My repository:
#Repository
public interface CatalogRepository<T extends Catalog> extends JpaRepository<T, Integer>{
}
My service:
#Service
#Transactional
public class CatalogServiceImpl<T extends Catalog,Dto extends CatalogDto>{
private final Logger log = LoggerFactory.getLogger(CatalogServiceImpl.class);
private final CatalogRepository<T> repository;
private CatalogMapper<T,Dto> catalogMapper=new CatalogMapper<T,Dto>() {};
public CatalogServiceImpl(CatalogRepository<T> repository) {
this.repository = repository;
}
}
My Mapper:
public abstract class CatalogMapper<T extends Catalog,Dto extends CatalogDto> implements Rapper<T,Dto> {
#Override
public Dto entityToDto(T entity) {
return null;
}
#Override
public T dtoToEntity(Dto dto) {
return null;
}
}
I want to create an object from T in dtoToEntity method and an object from Dto in entityToDto method in CatalogMapper class
I think that these two methods should be abstract because every mapper probably works in different ways. Anyway you can provide a base implementation like this
public T dtoToEntity(Dto dto) throws InstantiationException, IllegalAccessException {
T entity = (T) ((Class)((ParameterizedType)this.getClass().
getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
BeanUtils.copyProperties(dto, entity);
return entity;
}
public Dto entityToDto(T entity) throws InstantiationException, IllegalAccessException {
Dto dto = (Dto) ((Class)((ParameterizedType)this.getClass().
getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();
BeanUtils.copyProperties(entity, dto);
return dto;
}
Using ParameterizedType of the generic class you can create a new instance and then execute a simple copyProperties

How to get the Spring Data JPA Repository Factory?

Since I got no answer to my previous question I tried to tweak the example given in the Spring documentation for customizing repositories. There ist a Method getRepository(Class repositoryInterface) which looks like It ist the right place to map my repository Overrides:
public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory<>(entityManager);
}
private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
#Resource
private Map<Class<?>, Class<?>> overrideRepositories;
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
//Test
overrideRepositories = new HashMap<>();
overrideRepositories.put(CustomerRepository.class, Customer2Repository.class);
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return super.getTargetRepository(metadata);
// return new MyRepositoryImpl<T, I>((Class<T>)
// metadata.getDomainClass(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the
// JpaRepositoryFactory
// to check for QueryDslJpaRepository's which is out of scope.
return JpaRepository.class;
}
#SuppressWarnings("unchecked")
#Override
public <E> E getRepository(Class<E> repositoryInterface, Object customImplementation) {
if (overrideRepositories != null) {
Class<?> override = overrideRepositories.get(repositoryInterface);
if (override != null) {
repositoryInterface = (Class<E>) override;
}
}
return super.getRepository(repositoryInterface, customImplementation);
}
}
}
I configured it like this: #EnableJpaRepositories(repositoryFactoryBeanClass=MyRepositoryFactoryBean.class)
Normally you would autowire the repositories themselves which doesn't work because there are two Interfaces with the same Type and I don't know how to tell Spring which one to use.
If I autowire the factory instead, I can call getRepository each time I need a specific one. But how do I get this factory? Does Spring Data JPA somehow expose this as a bean? I can't find anything on google concerning this. Or is this approach entirely wrong?
You can use the ApplicationContext instance to get your MyRepositoryFactoryBean bean class. All you have to do is implement the ApplicationContextAware interface in order to get access to the ApplicationContext instance.
public class myClass implements ApplicationContextAware{
private static ApplicationContext ac;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ac = applicationContext;
}
}
Now you can use ac.getBean("MyRepositoryFactoryBean") to get the factory directly from the ApplicationContext. Once you have that bean you can call getRepository on it.

GenericDAO inheritance from interface

I have an abstract DAO:
public abstract class AbstractJpaDAO<T extends Serializable> implements I_AbstractJpaDAO<T> {
private Class<T> clazz;
#PersistenceContext
protected EntityManager entityManager;
public final void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
#Override
public T findOne(final long id) {
return entityManager.find(clazz, id);
}
#Override
public List<T> findAll() {
return entityManager.createQuery("from " + clazz.getName()).getResultList();
}
#Override
public void create(final T entity) {
entityManager.persist(entity);
}
#Override
public T update(final T entity) {
return entityManager.merge(entity);
}
#Override
public void delete(final T entity) {
entityManager.remove(entity);
}
#Override
public void deleteById(final long entityId) {
final T entity = findOne(entityId);
delete(entity);
}
}
I then extend this DAO in each DAO implementation (code not included) but header something like:
public class UserDAOImpl extends AbstractJpaDAO <User> implements UserDAO {
.....
With each entity, I work with a base interface type, for this example,let's call it UserDAO, and have an associated implementation, let's call it, UserDAOIMPL. To avoid having to define the same methods each Interface to each DAO. As in this longwinded example, i.e :
public interface UserDAO {
User findOne(long id);
List<User> findAll();
void create(User user);
User update(User user);
void delete(User user);
void deleteById(long userID);
User findUserByUserName(String name);
EntityManager returnEntityManager();
}
I would like to instead create a base interface, that all DAOs can extend.
public interface I_AbstractJpaDAO<T> {
.....
}
and then use this in each DAO interface.
public interface userDAO extends I_AbstractJpaDAO<T> { .....
However, I'm getting problems with "both methods have erasure, but neither overrides the other". Something to do with the Serialization thing I suspect. Can anyone help me please?
It means that, your base interface and abstract interface have the methods with the same signature and different return type.
Try this:
public interface userDAO extends I_AbstractJpaDAO<User> { .....

Spring 3.2 Autowire generic types

So I have a number of generics in Spring 3.2 and ideally my architecture would look something like this.
class GenericDao<T>{}
class GenericService<T, T_DAO extends GenericDao<T>>
{
// FAILS
#Autowired
T_DAO;
}
#Component
class Foo{}
#Repository
class FooDao extends GenericDao<Foo>{}
#Service
FooService extends GenericService<Foo, FooDao>{}
Unfortunately with multiple implementations of the generics the autowiring throws an error about multiple matching bean definitions. I assume this is because #Autowired processes before type erasure. Every solution I've found or come up with looks ugly to me or just inexplicably refuses to work. What is the best way around this problem?
How about adding a constructor to the GenericService and move the autowiring to the extending class, e.g.
class GenericService<T, T_DAO extends GenericDao<T>> {
private final T_DAO tDao;
GenericService(T_DAO tDao) {
this.tDao = tDao;
}
}
#Service
FooService extends GenericService<Foo, FooDao> {
#Autowired
FooService(FooDao fooDao) {
super(fooDao);
}
}
Update:
As of Spring 4.0 RC1, it is possible to autowire based on generic type, which means that you can write a generic service like
class GenericService<T, T_DAO extends GenericDao<T>> {
#Autowired
private T_DAO tDao;
}
and create multiple different Spring beans of it like:
#Service
class FooService extends GenericService<Foo, FooDao> {
}
Here is a closest solution. The specialized DAOs are annotated at the business layer. As in the question from OP, the best effort would be having an annotated DAO in the EntityDAO generic template itself. Type erasure seems to be not allowing the specialized type information to get passed onto the spring factories [resulting in reporting matching beans from all the specialized DAOs]
The Generic Entity DAO template
public class EntityDAO<T>
{
#Autowired
SessionFactory factory;
public Session getCurrentSession()
{
return factory.getCurrentSession();
}
public void create(T record)
{
getCurrentSession().save(record);
}
public void update(T record)
{
getCurrentSession().update(record);
}
public void delete(T record)
{
getCurrentSession().delete(record);
}
public void persist(T record)
{
getCurrentSession().saveOrUpdate(record);
}
public T get(Class<T> clazz, Integer id)
{
return (T) getCurrentSession().get(clazz, id);
}
}
The Generic Entity Based Business Layer Template
public abstract class EntityBusinessService<T>
implements Serializable
{
public abstract EntityDAO<T> getDAO();
//Rest of code.
}
An Example Specialized Entity DAO
#Transactional
#Repository
public class UserDAO
extends EntityDAO<User>
{
}
An Example Specialized Entity Business Class
#Transactional
#Service
#Scope("prototype")
public class UserBusinessService
extends EntityBusinessService<User>
{
#Autowired
UserDAO dao;
#Override
public EntityDAO<User> getDAO()
{
return dao;
}
//Rest of code
}
You can remove the #autowire annotation and perform delayed “autowire” using #PostConstruct and ServiceLocatorFactoryBean.
Your GenericService will look similar to this
public class GenericService<T, T_DAO extends GenericDao<T>>{
#Autowired
private DaoLocator daoLocatorFactoryBean;
//No need to autowried, autowireDao() will do this for you
T_DAO dao;
#SuppressWarnings("unchecked")
#PostConstruct
protected void autowireDao(){
//Read the actual class at run time
final Type type;
type = ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[1];
//figure out the class of the fully qualified class name
//this way you can know the bean name to look for
final String typeClass = type.toString();
String daoName = typeClass.substring(typeClass.lastIndexOf('.')+1
,typeClass.length());
daoName = Character.toLowerCase(daoName.charAt(0)) + daoName.substring(1);
this.dao = (T_DAO) daoLocatorFactoryBean.lookup(daoName);
}
daoLocatorFactoryBean does the magic for you.
In order to use it you need to add an interface similar to the one below:
public interface DaoLocator {
public GenericDao<?> lookup(String serviceName);
}
You need to add the following snippet to your applicationContext.xml
<bean id="daoLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.DaoLocator" />
</bean>
This is a nice trick and it will save you little boilerplate classes.
B.T.W I do not see this boilerplate code as a big issue and the project I working for uses matsev approach.
Why do you want a generic service ? Service classes are meant for specific units of work involving multple entities. You can just inject a repository straight into a controller.
Here is an example of generic repository with constructor argument, you could also make each method Generic instead and have no constructor argument. But each method call would require class as parameter:
public class DomainRepository<T> {
#Resource(name = "sessionFactory")
protected SessionFactory sessionFactory;
public DomainRepository(Class genericType) {
this.genericType = genericType;
}
#Transactional(readOnly = true)
public T get(final long id) {
return (T) sessionFactory.getCurrentSession().get(genericType, id);
}
Example of bean definition for the generic repository - you could have multple different beans, using different contstructor args.
<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
<constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>
Depdncy injection of bean using resource annotation
#Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;
And this allows the Domainreposiroty to be subclassed for specific entities/methods, which woul dallow autowiring :
public class PersonRepository extends DomainRepository<Person> {
public PersonRepository(){
super(Person.class);
}
...
You should use autowiring in classes which extends these generics
For this question one needs to understand about what autowire is. In common terms we can say that through autowire we create a object instance/bean at the time of deployment of the web app. So now going with the question if you are declaring autowiring in multiple places with the same name. Then this error comes. Autowiring can be done in multiple ways so if you are using multiple type of autowiring technique, then also one could get this error.
Complete Generic Solution using Spring 4:
Domain Class
#Component
class Foo{
}
#Component
class Bar{
}
DAO Layer
interface GenericDao<T>{
//list of methods
}
class GenericDaoImpl<T> implements GenericDao<T>{
#Autowired
SessionFactory factory;
private Class<T> domainClass; // Get Class Type of <T>
public Session getCurrentSession(){
return factory.getCurrentSession();
}
public DaoImpl() {
this.domainClass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), DaoImpl.class);
}
//implementation of methods
}
interface FooDao extends GenericDao<Foo>{
//Define extra methods if required
}
interface BarDao extends GenericDao<Bar>{
//Define extra methods if required
}
#Repository
class FooDao extends GenericDaoImpl<Foo> implements FooDao{
//implementation of extra methods
}
#Repository
class BarDao extends GenericDaoImpl<Bar> implements BarDao{
//implementation of extra methods
}
Service Layer
interface GenericService<T>{
//List of methods
}
class GenericServiceImpl<T> implements GenericService<T>{
#Autowire
protected GenericDao<T> dao; //used to access DAO layer
}
class FooService extends GenericService<Foo>{
//Add extra methods of required
}
class BarService extends GenericService<Bar>{
//Add extra methods of required
}
#Service
class FooServiceImpl extends GenericServiceImpl<Foo> implements GenericService<Foo>{
//implementation of extra methods
}
#Service
class BarServiceImpl extends GenericServiceImpl<Bar> implements GenericService<Bar>{
//implementation of extra methods
}

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