I'm trying to excute the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DBDataManipulatorServiceTestContext.class, loader =
SpringApplicationContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class UserInterfaceBeanTest {
#Autowired
private DBDataManipulatorService dbDataManipulatorService;
.....
#Test
public void testGetDistinctWorkPackageId() {
WorkPackage workPackage1 = new WorkPackage();
workPackage1.setWorkPackageId("TEST1");
workPackage1.setWorkPackageName("WP_1");
WorkPackage workPackage2 = new WorkPackage();
workPackage2.setWorkPackageId("TEST2");
workPackage2.setWorkPackageName("WP_2");
WorkPackage workPackage3 = new WorkPackage();
workPackage3.setWorkPackageId("TEST3");
workPackage3.setWorkPackageName("WP_3");
WorkPackage workPackage4 = new WorkPackage();
workPackage4.setWorkPackageId("TEST4");
workPackage4.setWorkPackageName("WP_4");
Aircraft aircraft1 = new Aircraft();
aircraft1.setAircraftId(50001);
aircraft1.setModel("100");
Aircraft aircraft2 = new Aircraft();
aircraft2.setAircraftId(50002);
aircraft2.setModel("100");
aircraft1.addOrUpdateWorkPackageToAircraft(workPackage1);
aircraft1.addOrUpdateWorkPackageToAircraft(workPackage3);
aircraft2.addOrUpdateWorkPackageToAircraft(workPackage2);
aircraft2.addOrUpdateWorkPackageToAircraft(workPackage3);
Map<Integer, Aircraft> aircraftsMap = new HashMap<Integer, Aircraft>();
aircraftsMap.put(50001, aircraft1);
aircraftsMap.put(50002, aircraft2);
UserInterfaceBean userInterfaceBean = new UserInterfaceBean();
userInterfaceBean.setModel("100");
aircraftsMap = dbDataManipulatorService.saveToDataBase(aircraftsMap);
List<String> workPackgesIds = userInterfaceBean.getDistinctWorkPackageId();
assertTrue(workPackgesIds.contains("TEST1"));
assertTrue(workPackgesIds.contains("TEST2"));
assertTrue(workPackgesIds.contains("TEST3"));
assertTrue(!workPackgesIds.contains("TEST4"));
}
But I get NullPointerException due to dbDataManipulatorService that is not correctly injected, my serviceContext is the following:
#ComponentScan
#EnableAutoConfiguration
#EnableBatchProcessing
#EnableJpaRepositories
public class DBDataManipulatorServiceContext {
.....
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setName("aircraft").setType(H2)
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new
LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com");
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter =
new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
.....
Can anyone tell me where is the problem? and how to solve it.
EDIT
Here's my DBDataManipulatorServiceImpl:
#Service
#Transactional
public class DBDataManipulatorServiceImpl implements DBDataManipulatorService {
.....
}
And here's it's interface:
public interface DBDataManipulatorService {
public AircraftRepository getAircraftRepository();
public Aircraft getAircraftById(String id);
public Map<Integer, Aircraft> saveToDataBase(Map<Integer, Aircraft> aircrafts);
public MyJob findJobByJobId(String jobId);
public WorkPackage findWorkPackageByAircraftIdAndWorkPackageId
(int aircraftId, String workPackageId);
public Aircraft findAircraftByaircraftId(int aircraftId);
public List<Aircraft> findAllAircrafts();
public List<String> findAllAvailableAircraftsModels();
public List<Aircraft> findAllAircraftsByModel(String model);
public List<WorkPackage> getWorkPackagesByAircraftModel(String model);
}
You have a #ComponentScan but you have not put the name of the package to scan from. The default behavior is to scan from the package where you have put the #ComponentScan essentially the package of your DBDataManipulatorServiceContext config class. I am guessing this is not where your DBDataManipulatorService is, if so just specify a package name to your #ComponentScan
The #Autowired annotation by default means that a bean must have been found otherwise you would have gotten a bean not found exception.
There are a few things that can cause this:
The unit test is instantiated via new DBDataManipulatorServiceContext()instead of being scanned as a Spring Bean, in this case #Autowired is ignored.
There is some XML configuration that overrides the annotations configuration, and so the bean is first created with autowiring on, and then later is discarded by a bean defined in XML with no parameter provided for the missing property
You need to mark the DBDataManipulatorService class somehow. Use #service or #component annotation over class name. This will tell spring to manage the class and thus to inject it in your test.
Related
I have two projects one with DAO classes and Model and another with Rest Controller
Project A : DAO Classes + Model
Project B : Rest Controller
Project A
application.properties:
spring.abcDatasource.url=
spring.abcDatasource.username=
spring.abcDatasource.password=
spring.abcDatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.xyzDatasource.url=
spring.xyzDatasource.username=
spring.xyzDatasource.password=
spring.xyzDatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.initialize=false
DBConfiguration.java
#Configuration
public class DBConfiguration {
#Primary
#Bean(name = "abcDS")
#ConfigurationProperties(prefix = "spring.abcDatasource")
public DataSource abcDS() {
return DataSourceBuilder.create().build();
}
#Bean(name = "abcJdbc")
public JdbcTemplate abcJdbcTemplate(#Qualifier("abcDS") DataSource abcDS) {
return new JdbcTemplate(abcDS);
}
#Bean(name = "xyzDS")
#ConfigurationProperties(prefix = "spring.xyzDatasource")
public DataSource xyzDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "xyzJdbc")
public JdbcTemplate ebsJdbcTemplate(#Qualifier("xyzDS") DataSource xyzDatasource) {
return new JdbcTemplate(xyzDatasource);
}
}
AlphaDAO.Java
#Repository
public class AlphaDAO{
#Autowired
#Qualifier("abcJdbc")
private JdbcTemplate abcJdbc;
#Autowired
#Qualifier("xyzJdbc")
private JdbcTemplate xyzJdbc;
SqlParameterSource namedParameters;
public Collection<Alpha> findAll(String owner){
String sql = "SELECT * from alpha where OWNER in (:owner)" ;
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(abcJdbc.getDataSource());
namedParameters = new MapSqlParameterSource("owner", owner);
List<Alpha> list = namedParameterJdbcTemplate.query(sql,namedParameters,
new BeanPropertyRowMapper(Alpha.class));
return list;
}
Project B Rest Controller :
AlphaServiceApplication.java
#SpringBootApplication
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class AlphaServiceApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AlphaServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(AlphaServiceApplication.class, args);
}
}
AlphaServiceController.java
#RestController
public class AlphaServiceController {
private static final Logger logger = LoggerFactory.getLogger(AlphaServiceController.class);
#Autowired
AlphaDAO dao;
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping("/alpha")
public Collection<Alpha> index(#RequestBody String owner) {
return dao.findAll(owner);
}
If I try to run the rest controller I am getting the error saying
APPLICATION FAILED TO START
Description:
Field dao in com.xyz.web.wip.AlphaService.AlphaServiceController required a bean of type 'com.xyz.comp.wip.alphaComp.dao.AlphaDAO' that could not be found.
Action:
Consider defining a bean of type 'com.xyz.comp.wip.alphaComp.dao.AlphaDAO' in your configuration.
Your AlphaDao class doesnt make much sense, you are trying to autowire two fields but you still have a constructor.
Spring cant build the object because there is no qualifier on the constructor.
You can either do constructor injection or field injection but you shouldn’t use both.
I would recommend using constructor injection.
#Repository
public class AlphaDAO{
private final JdbcTemplate abcJdbc;
private final JdbcTemplate xyzJdbc;
#Autowired
public AlphaDAO(
#Qualifier("abcJdbc") JdbcTemplate abcJdbc,
#Qualifier("xyzJdbc") JdbcTemplate xyzJdbc){
this.abcJdbc = abcJdbc;
this.xyzJdbc = xyzJdbc;
}
Also remove your #Bean method from the controller.
Since the DAO classes and Rest Controller is in different packages. Added scanBasePackages to #SpringBootApplication annotation with one level up worked fine.
AlphaServiceApplication.java
#SpringBootApplication(scanBasePackages = { "com.xyz" })
I am trying to build a standalone desktop spring application, the application should get the information from multiple data sources. The problem I facing is that #Autowired Repository is null Bellow is my project setup, I thinking because of the application is desktop I am missing some configuration
Main Class
#SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.headless(false)
.web(false)
.run(args);
}
#Bean
public Main frame() {
return new Main();
}
}
DB Config Second is configured the same way. Additionally here IntelliJ shows that EntityManagerFactoryBuilder builder bean cannot be autowired
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "notificationEntityManagerFactory",
basePackages = { "org.thebot.notification.repository" })
public class NotificationDBConfig {
#Primary
#Bean(name = "notificationDataSource")
#ConfigurationProperties(prefix="spring.notificationDatasource")
public DataSource notificationDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "notificationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean notificationEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("notificationDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("org.thebot.notification.domain")
.persistenceUnit("notification")
.build();
}
#Primary
#Bean(name = "notificationTransactionManager")
public PlatformTransactionManager notificationTransactionManager(
#Qualifier("notificationEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Repository
#Repository
public interface NotificationRepository extends JpaRepository<Notification, Long> {
}
Place where the repository is needed
public class NotificationTab {
#Autowired
private PopulationRepository populationRepository;
private JPanel notificationPanel;
}
I have checked many SO comments and the docs for spring data and unit testing but I cant get this to work and I dont know why its not working.
I have a junit test class that looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class DealServiceTest {
#Configuration
static class ContextConfiguration {
// this bean will be injected into the OrderServiceTest class
#Bean
public DealService orderService() {
DealService dealService = new DealService();
// set properties, etc.
return dealService;
}
#Bean
public EmployeeService employeeService(){
EmployeeService employeeService = new EmployeeService();
return employeeService;
}
}
#Autowired
DealService dealService;
#Autowired
EmployeeService employeeService;
#Test
public void createDeal() throws ServiceException {
Employee employee = new Employee("Daniel", "tuttle", "danielptm#me.com", "dannyboy", "secret password", 23.234, 23.23);
Deal d = dealService.createDeal("ADSF/ADSF/cat.jpg", "A title goes here", "A deal description", 23.22, "Name of business", 23.23,23.23, employee, "USA" );
Assert.assertNotNull(d);
}
}
And then I have my service class that looks like this
#Service
public class DealService {
#Autowired
private DealRepository dealRepository;
public Deal createDeal(String image, String title, String description, double distance, String location, double targetLat, double targetLong, Employee employee, String country) throws ServiceException {
Deal deal = new Deal(image, title, description, distance, location, targetLat, targetLong, employee, country);
try {
return dealRepository.save(deal);
}catch(Exception e){
throw new ServiceException("Could not create a deal: "+deal.toString(), e);
}
}
public Deal updateDeal(Deal d) throws ServiceException {
try{
return dealRepository.save(d);
}catch(Exception e){
throw new ServiceException("Could not update deal at this time: "+d.toString(),e);
}
}
public List<Deal> getAllDealsForEmployeeId(Employee employee) throws ServiceException {
try{
return dealRepository.getAllDealsBy_employeeId(employee.getId());
}catch(Exception e){
throw new ServiceException("Could not get deals for employee: "+employee.getId(), e);
}
}
}
And then my repository:
*/
public interface DealRepository extends CrudRepository<Deal, Long>{
public List<Deal> getDealsBy_country(String country);
public List<Deal> getAllDealsBy_employeeId(Long id);
}
My config file looks like this:
#Configuration
#EnableJpaRepositories("com.globati.repository")
#EnableTransactionManagement
public class InfrastructureConfig {
#Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/DatabaseProject");
config.setUsername("awesome");
config.setPassword("database");
return new HikariDataSource(config);
}
// #Bean
// public DataSource derbyDataSource(){
// HikariConfig config = new HikariConfig();
// config.setDriverClassName("jdbc:derby:memory:dataSource");
// config.setJdbcUrl("jdbc:derby://localhost:1527/myDB;create=true");
// config.setUsername("awesome");
// config.setPassword("database");
//
// return new HikariDataSource(config);
//
// }
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setGenerateDdl(true);
return adapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource()); //Get data source config here!
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setPackagesToScan("com.globati.model");
return factory;
}
}
But I get this error.
java.lang.IllegalStateException: Failed to load ApplicationContext ...
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean found for dependency
[com.globati.repository.DealRepository]: expected at least 1 bean
which qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Any suggestions for how I can successfully do unit testing with spring data, junit and with my service and repositories would be greatly appreciated. Thanks!
For a repository bean to be injected,
You need to enable Repositories, using one of the spring-data annotations. So add #Enable*Repositories to your configuration class
You also need dB factories and other related beans configured. I am using Mongo and I have mongoDbFactory bean configured
And for the most part your test configuration should look like your main configuration except for unnecessary bean replaced by mock implementations
UPDATE
Here is my code (sorry mine is in mongo, I think you can relate)
#Configuration
#WebAppConfiguration
#ComponentScan(basePackages = "com.amanu.csa",
excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebConfig.class))
#EnableMongoRepositories(repositoryImplementationPostfix = "CustomImpl")
class TestConfig {
#Bean
Mongo mongo() throws Exception {
return new MongoClient("localhost")
}
#Bean
MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(mongo(), "csa_test")
}
#Bean
MongoTemplate mongoTemplate() throws Exception {
MongoTemplate template = new MongoTemplate(mongoDbFactory())
template.setWriteResultChecking(WriteResultChecking.EXCEPTION)
return template
}
}
That is my test config file... As you can see it explicitly excludes my main configuration file.
#ContextConfiguration(classes = TestConfig)
#RunWith(SpringRunner.class)
class OrganizationServiceTest {
#Autowired
OrganizationService organizationService
#Test
void testRegister() {
def org = new Organization()
//...
organizationService.register(org)
// ...
}
And that is my test class. It refers to the test config, and I recommend using named config classes. You can put common options onto a super class and extend those and use them for your tests.
I hope this helps
You can try to add
#ActiveProfiles("your spring profile")
In addition I would suggest to use an embedded test db like flapdoodle (https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo)
You could either :
Mock your repository methods (Using Mockito for example)
Using an embedded database for your unit tests
I'm trying to solve a problem with two datasources. I read many text in the internet and made simple solution based on this source
I've made two "config classes", like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "ResultEntityManagerFactory",
transactionManagerRef = "ResultEntityTransactionManager",
basePackages = "com.migr.result.repositories"
)
public class ResultTablesConfig {
#Autowired
#Qualifier("orclAdapter")
JpaVendorAdapter jpaVendorAdapter;
#Bean(name="ResultEntityDataSource")
#ConfigurationProperties(prefix = "datasource.migr.result")
public DataSource declReaconDS() {
return new DataSource();
}
#Bean(name = "ResultEntityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
#Bean(name = "ResultEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(declReaconDS());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.migr.result.tables.*");
lef.setPersistenceUnitName("ResultEntityPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
#Bean(name = "ResultEntityTransactionManager")
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
}
and another one, practically same - SourceTablesConfig.
only difference is #Bean names (they all start with "Source") and repositories mapping, like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "SourceEntityManagerFactory",
transactionManagerRef = "SourceEntityTransactionManager",
basePackages = "com.migr.source.repositories"
)
public class SourceTablesConfig {
...............
#Bean(name = "SourceEntityManagerFactory")
public EntityManagerFactory entityManagerFactory(){
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(declReaconDS());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.migr.source.tables.*");
lef.setPersistenceUnitName("SourceEntityPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
My services are simle:
#Service
public class CatalogsSourceService {
#Autowired
CatalogsSourceRepository catalogsrepository;
.......
}
where CatalogsSourceRepository extends JpaRepository<Catalogs, Long>
My "main" class:
#RestController
#RequestMapping("/")
public class Main {
#Autowired
CatalogsSourceService source_serv;
#Autowired
CatalogsResultService result_serv;
#RequestMapping
public Object index() {
Map<String,List> results = new HashMap<>();
results.put("First", source_serv.findAll());
results.put("Second", source_serv.findAll());
return results;
}
it worked perfectly when I was having only one DataSource,
but now it exits with error:
expected single matching bean but found 2: SourceEntityManagerFactory,ResultEntityManagerFactory
I don't get why it is happening. My solution looks practically like mentioned in above link, but his do not get such error.
I will be very gratefull for any help
PS. forgot about my "AppConfig":
#Configuration
#ComponentScan(basePackages = { "com.migr" })
public class AppConfig {
#Bean(name="orclAdapter")
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setDatabase(Database.ORACLE);
return jpaVendorAdapter;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Sorry guys, my problem is solved. I missed easiest solution).
I just had to annotate one EntityManagerFactory bean with #Primary annotation.
#Bean(name = "SourceEntityManagerFactory")
#Primary
public EntityManagerFactory entityManagerFactory() {
...........
}
obvious solutions are not always so obvious...
I am using Spring and Hibernate and I am successfully autowiring a Repository inside the contructor of a Service class. When i try to add #Transactional methods in my Service class i get an AopConfigException that it is about the generation of CGLib class.
My Configuration consists in 3 files.
An AppInitializer class that implements WebApplicationInitializer , a WebMvcConfig class that extends WebMvcConfigurerAdapter and lastly a PersistentContext class.
AppInitializer
public class AppInitializer implements WebApplicationInitializer {
private static final String CONFIG_LOCATION = "com.project.app.config";
private static final String MAPPING_URL = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",
new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(MAPPING_URL);
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation(CONFIG_LOCATION);
return context;
}
WebMvcConfig
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "com.project.app" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private Environment env;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("hello");
}
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
PersistentContext
#Component
#EnableJpaRepositories("com.project.app.services.repositories")
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class PersistenceContext {
#Autowired
private Environment env;
#Bean
#Primary
public DataSource dataSource() throws ClassNotFoundException {
DataSource ds = new DataSource();
ds.setUrl(env.getProperty(SystemSettings.DS_URL));
ds.setUsername(env.getProperty(SystemSettings.DS_USERNAME));
ds.setPassword(env.getProperty(SystemSettings.DS_PASSWORD));
ds.setDriverClassName(env.getProperty(SystemSettings.DS_DRIVER));
return ds;
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.project.app.services.entities");
// .. Set Properties..
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
The Repository INTERFACE extends CrudRepository
StopRepository
#Repository
#RepositoryRestResource(collectionResourceRel = "stop", path = "stop")
public interface StopRepository extends CrudRepository<StopJPA, Long> {
#Override
StopJPA save(StopJPA persisted);
}
The Entity class.
StopJPA
#Entity
#Table(name = "stop")
public class StopJPA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "stop_description")
private String stopDescription;
#Column(name = "id_stop", nullable = false)
private String idStop;
public StopJPA() {
}
public StopJPA(String stopDescription, String idStop) {
this.stopDescription = stopDescription;
this.idStop = idStop;
}
// .. Getters & Setters ..
}
And the Service class implementation:
StopService
#Service
final class RepoStopService {
private StopRepository stopRepository;
#Autowired
RepoStopService(StopRepository stopRepository) {
this.stopRepository = stopRepository;
}
#Transactional
public StopDTO create(StopDTO newEntry) {
StopJPA created = new StopJPA(newEntry.getStopDescription(), newEntry.getIdStop());
created = stopRepository.save(created);
return EntitiesConverter.mapEntityIntoDTO(created);
}
}
Unfortunately when i try to run it on server i get this exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'repoStopService' defined in file [C:..\RepoStopService.class]: Initialization of bean failed;
Caused by: org.springframework.aop.framework.AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class ..RepoStopService
I understand that Spring uses either JDK proxy or CGLib. The default behavior if we autowire an interface (here its the repository) is to have a JDK proxy?
what need to be changed in order to make it work?
The exception message is quite clear
AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService
First of all it is complaining about the fact that it cannot create a proxy for your service it doesn't mention your repositories at all. Your service is a class without an interface and it is also final.
The #Transactional is on a method in your service. Spring needs to create a proxy for your service to be able to start and commit the transaction at that point. As your service isn't implementing an interface it tries to create a class based proxy, however it cannot because your class is marked final.
To fix, remove the final or create an interface to be able to use JDK Dynamic proxies instead of Cglib based class proxies.
Note: Depending on the version of Spring used, it will still fail when removing the final keyword as on earlier versions it is also required to have a no-arg default constructor and you only have a single argument constructor.