Makarov Oleg Makarov Oleg - 1 month ago 6
Java Question

DAO and Spring Autowired

I tried to create an abstract Dao. I use Spring + Hibernate.
Here's my code.

Main class with configuration:

package ru.makaek.growbox.api;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@ComponentScan(value = "ru.makaek.growbox")
@EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)
@EnableTransactionManagement
@SpringBootApplication
public class Application {

@Autowired
private Environment env;

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}


@Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("datasource.driver"));
dataSource.setUrl(env.getRequiredProperty("datasource.url"));
dataSource.setUsername(env.getRequiredProperty("datasource.username"));
dataSource.setPassword(env.getRequiredProperty("datasource.password"));
return dataSource;
}

@Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan(new String[]{"ru.makaek.growbox"});
return sessionFactory;
}

@Bean
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}

}


Rest controller

package ru.makaek.growbox.api.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ru.makaek.growbox.api.model.data.entities.Device;
import ru.makaek.growbox.api.service.IStructureService;

@RestController
public class DeviceController extends AbstractController {

@Autowired
IStructureService structureService;

@RequestMapping(value = "/devices", method = RequestMethod.POST)
public Answer addDevice(@RequestBody Device device) {
structureService.addDevice(device);
return ok("Device has been added");
}

@RequestMapping(value = "/devices", method = RequestMethod.GET)
public Answer getDevices() {
return ok(structureService.getDevices());
}

@RequestMapping(value = "/devices/{deviceId}", method = RequestMethod.GET)
public Answer getDevice(@PathVariable Long deviceId) {
return ok(structureService.getDevice(deviceId));
}

}


Service layer. Interface

package ru.makaek.growbox.api.service;

import ru.makaek.growbox.api.model.data.entities.Device;

import java.util.List;

public interface IStructureService {

void addDevice(Device device);

List<Device> getDevices();

Device getDevice(Long deviceId);
}


Service layer. Implementation

package ru.makaek.growbox.api.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.makaek.growbox.api.model.data.dao.base.IDao;
import ru.makaek.growbox.api.model.data.entities.Device;

import java.util.List;

@Service
@Transactional
public class StructureService implements IStructureService {

IDao<Device> deviceDao;

@Autowired
public void setDao(IDao<Device> dao) {
deviceDao = dao;
dao.setClazz(Device.class);
}

@Override
public void addDevice(Device device) {
deviceDao.create(device);
}

@Override
public List<Device> getDevices() {
return deviceDao.findAll();
}

@Override
public Device getDevice(Long deviceId) {
return deviceDao.findOne(deviceId);
}
}


Entity

package ru.makaek.growbox.api.model.data.entities;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity(name = "devices")
@Data public class Device extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}


DAO. Interface

package ru.makaek.growbox.api.model.data.dao.base;

import ru.makaek.growbox.api.model.data.entities.BaseEntity;

import java.util.List;

public interface IDao<T extends BaseEntity> {

T findOne(final long id);

void setClazz(Class<T> clazz);

List<T> findAll();

void create(final T entity);

T update(final T entity);

void delete(final T entity);

void deleteById(final long entityId);

}


Abstract DAO

package ru.makaek.growbox.api.model.data.dao.base;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ru.makaek.growbox.api.model.data.entities.BaseEntity;
import ru.makaek.growbox.api.util.GBException;

import java.util.List;

public abstract class AbstractDao<T extends BaseEntity> implements IDao<T> {

private Class<T> clazz;

@Autowired
private SessionFactory sessionFactory;

public final void setClazz(Class<T> clazz) {
this.clazz = clazz;
}

public T findOne(long id) {
try {
return (T) getCurrentSession().get(clazz, id);
} catch (Exception e) {
throw new GBException.InternalError(e.getMessage());
}
}

public List<T> findAll() {
try {
return getCurrentSession().createQuery("from " + clazz.getName()).list();
} catch (Exception e) {
throw new GBException.InternalError(e.getMessage());
}
}

public void create(T entity) {
try {
getCurrentSession().persist(entity);
} catch (Exception e) {
throw new GBException.InternalError(e.getMessage());
}
}

public T update(T entity) {
try {
return (T) getCurrentSession().merge(entity);
} catch (Exception e) {
throw new GBException.InternalError(e.getMessage());
}
}

public void delete(T entity) {
try {
getCurrentSession().delete(entity);
} catch (Exception e) {
throw new GBException.InternalError(e.getMessage());
}
}

public void deleteById(long entityId) {
try {
T entity = findOne(entityId);
delete(entity);
} catch (Exception e) {
throw new GBException.InternalError(e.getMessage());
}
}

protected final Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}


}


DAO. Implementation

package ru.makaek.growbox.api.model.data.dao;

import org.springframework.stereotype.Repository;
import ru.makaek.growbox.api.model.data.dao.base.AbstractDao;
import ru.makaek.growbox.api.model.data.entities.Device;

@Repository
public class DeviceDao extends AbstractDao<Device> {
}


I have one trouble. When I call GET http://host:port/devices API method I have null in the clazz variable in the AbstractDao.findAll() method. When I was debugging the code i found one interesting thing: in the service layer method deviceDao.getClazz() returned needed clazz (not null). But in method AbstractDao.findAll() I have null in clazz variable. Why? Please help.

Sorry for my English and formulation. I'm new in this site, Spring and English

Answer Source

You need to remove 'final' from setClazz method.

For an explanation on this behavior see Spring AOP CGLIB proxy's field is null which explains how Spring used CGLIB to proxy it (can see it's CGLIB proxy class in debugger) using a dynamically generated subclass; and by using final methods "CGLIB therefore cannot override them to delegate to the real instance" hence it returns the uninitialized value.