Why can't Service find my Repository bean? - spring-boot

I'm making a simple spring boot application and my service doesn't seem to notice my repository bean for some reason. I checked my configuration and it seems fine, I also basically just copied way that the repository is injected from a tutorial so i really don't even have an idea where might the problem be.
My Repository:
#Repository
public interface ProjectRepository extends CrudRepository<Trucks, Integer>
{
#Query("....")
List<Trucks> getTrucks();
}
My service:
#Service
#Transactional
public class ProjectServiceImpl implements ProjectService {
private ProjectRepository projectRepository;
#Autowired
public ProjectServiceImpl(ProjectRepository projectRepository) {
this.projectRepository = projectRepository;
}
#Override
public List<Trucks> getTrucks() {
return projectRepository.getTrucks();
}
}
Configuration:
#Configuration
#EnableJpaRepositories(basePackages = {
"com.javar.domain"
})
#PropertySource("classpath:application.properties")
#EnableTransactionManagement
public class PersistanceContext {
private static final String[] ENTITY_PACKAGES = {
"com.javar.domain"
};
private static final String PROPERTY_NAME_DB_DRIVER_CLASS
="spring.datasource.driver-class-name";
private String PROPERTY_NAME_DB_URL="app.datasource.url";
private String PROPERTY_NAME_DB_USER="app.datasource.username";
private String PROPERTY_NAME_DB_PASSWORD="app.datasource.password";
private String
PROPERTY_NAME_HIBERNATE_DIALECT="spring.jpa.properties.hibernate.dialect";
private String
PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO="spring.jpa.hibernate.ddl-auto";
private String
PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY="spring.jpa.hibernate.naming-
strategy";
private String PROPERTY_NAME_HIBERNATE_SHOW_SQL="spring.jpa.show-sql";
private String
PROPERTY_NAME_HIBERNATE_FORMAT_SQL=
"spring.jpa.properties.hibernate.format_sql";
#Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
HikariConfig dataSourceConfig = new HikariConfig();
dataSourceConfig.setDriverClassName(env.getRequiredProperty
(PROPERTY_NAME_DB_DRIVER_CLASS));
dataSourceConfig.setJdbcUrl(env.getRequiredPropert
(PROPERTY_NAME_DB_URL));
dataSourceConfig.setUsername(env.getRequiredProperty
(PROPERTY_NAME_DB_USER));
dataSourceConfig.setPassword(env.getRequiredProperty
(PROPERTY_NAME_DB_PASSWORD));
return new HikariDataSource(dataSourceConfig);
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource
dataSource, Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new
LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new
HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan(ENTITY_PACKAGES);
Properties jpaProperties = new Properties();
//Configures the used database dialect. This allows Hibernate to create SQL
//that is optimized for the used database.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
//Specifies the action that is invoked to the database when the Hibernate
//SessionFactory is created or closed.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
//Configures the naming strategy that is used when Hibernate creates
//new database objects and schema elements
jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
//If the value of this property is true, Hibernate writes all SQL
//statements to the console.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
//If the value of this property is true, Hibernate will use prettyprint
//when it writes SQL to the console.
jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
Application.properties:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
app.datasource.url=jdbc:mysql://localhost:3306/zavrsni?useSSL=false
app.datasource.username=....
app.datasource.password=....
#Hibernate Configuration
spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.hibernate.naming-strategy =
org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
And the error report:
Description:
Parameter 0 of constructor in com.javar.serviceImpl.ProjectServiceImpl
required a bean of type 'com.javar.repositoryy.ProjectRepository' that
could not be found.
Action:
Consider defining a bean of type 'com.javar.repositoryy.ProjectRepository'
in your configuration.
}

You can try this:
#Autowired
private ProjectRepository projectRepository;
and remove this contructor with that annotation:
#Autowired
public ProjectServiceImpl(ProjectRepository projectRepository) {
this.projectRepository = projectRepository;
}
If this helps you or you have some questions, let me edit this answer later.
UPDATED
Make sure that the package is correct where repository is found.
#EnableJpaRepositories(basePackages = {"com.javar.domain"})
So update the package to "com.javar.repositories"

Found the answer finally, so i just needed to add #EnableJpaRepositories(basePackages = "com.javar.repositoryy") above my Application class. Hope this helps someone in the future.

Solved this error by adding (missing in my case) maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Related

Spring Database configuration

I have faced with one problem that made me crazy in its resolution. The problem is following: when I configure connection with my database in java class - it works good i.e. tables in DB are created corresponding to my classes names. BUT. When I try to use application.properties file instead java class configuration, everything what names tables properly is ignored and I`m confused what can I do with this issue(
For instance: I have a class named as 'ATMResearcher'. When I use java class to configure, there is an 'ATMResearcher' table in my database. But when my configuration properties are described in app.properties file (without using configuration class), then there is an 'atm_researcher' table in DB
I hope, anybody helps me. I provide screenshots of my config class and application.properties file below.
application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/someDB
spring.datasource.username=name
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto=create
JpaConfig.java file:
#EnableJpaRepositories(basePackages = "repositories")
#Configuration
#EnableTransactionManagement
public class JpaConfig {
private LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("entities");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
private DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/someDB");
dataSource.setUsername("name");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory((EntityManagerFactory) entityManagerFactory());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
ATMResearcher.java
#Data
#Entity(name = "ATMResearcher")
#Table(name = "ATMResearcher")
#NamedQueries({
#NamedQuery(name = ATMResearcher.findByAtm, query = "from ATMResearcher i where i.atm=:name")})
public class ATMResearcher implements Serializable {
private static final long serialVersionUID = 2046903428407635527L;
public static final String findBy = "researcherAtm.findByAtm";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String researcher;
#ColumnDefault(value = "1")
private float volume;
#OneToMany(mappedBy = "atmReseacher", cascade = CascadeType.MERGE, targetEntity = ATMEval.class)
private Set<ATMEval> atmEval;
public String getShortName() {
return Arrays.stream(researcher.split(" ")).filter(n -> n.length() > 2)
.map(n -> n.substring(0, 1).toUpperCase()).collect(Collectors.joining());
}
#Override
public String toString() {
return "ATMResearcher{" +
"id=" + id +
", researcher='" + discipline + '\'' +
'}';
}
}
I think, you are missing this :
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
If it works, your properties should look like this:
spring.datasource.driverClassName=
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Sorry but I don't really get it what you mean by "when I configure connection with my database in java class - it works good".
But when looking at you code there is something that you might have not seen yet.
You are using camelcase in you table name. This results in table name "atm_reasearch".
#Data
#Entity(name = "ATMResearcher")
#Table(name = "ATMResearcher")
Hope this solves your problem.
Hello you need to declare the naming strategies in your application.yml or properties file
By default spring will have such behaviour:
Replace dots with underscores
Change camel case to snake case
Lower-case table names
You should use JPA compliant standart
For JPA 1.0
spring:
jpa:
hibernate:
naming:
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
For JPA 2.0
spring:
jpa:
hibernate:
naming:
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
For having same names use physical strategy

Spring Data with JPA not rolling back transaction on error

We have Spring Data configured for JPA. A Service Transaction method doesn't get rolled back for an error (e.g. a DB ConstraintViolationException).
The closest I could find was this
(Transaction not rolling back) Spring-data, JTA, JPA, Wildfly10
but we don't have any XML configuration, all of our configuration is Java-based.
Essentially, a service method looks like this: no errors are caught, everything thrown.
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false)
public void insertEvent() throws Exception {
// Part 1
EventsT event = new EventsT();
// populate it..
eventsDAO.save(event);
// Part 2 - ERROR HAPPENS HERE (Constraint Violation Exception)
AnswersT answer = new AnswersT();
// populate it..
answersDAO.save(answer);
}
Part 2 fails. But after the error and return, I see that the Event (Part 1) is still populated in the DB.
We also tried various combinations of #Transactional, nothing worked:
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false)
#Transactional(readOnly = false)
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = ConstraintViolationException.class, readOnly = false)
Spring Data CRUD DAO Interfaces:
#Repository
public interface EventsDAO extends JpaRepository<EventsT, Integer> {
}
#Repository
public interface AnswersDAO extends JpaRepository<AnswersT, Integer> {
}
JpaConfig:
#Configuration
#EnableJpaRepositories(basePackages = "com.myapp.dao")
#PropertySource({ "file:${conf.dir}/myapp/db-connection.properties" })
public class JpaConfig {
#Value("${jdbc.datasource}")
private String dataSourceName;
#Bean
public Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernate.dialect", PostgreSQL95Dialect.class.getName());
//props.put("hibernate.cache.provider_class", HashtableCacheProvider.class.getName());
return props;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() throws NamingException {
return new JpaTransactionManager( entityManagerFactory().getObject() );
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaPropertyMap(this.jpaProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
lef.setPackagesToScan("com.myapp.domain", "com.myapp.dao");
return lef;
}
#Bean
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup(dataSourceName);
}
}
Have there been any transaction rollback issues with Spring Data & JPA?
Believe it or not we fixed it. There were 2 parts to the solution:
1) Add #EnableTransactionManagement to JpaConfig as ledniov described, but that alone wasn't enough;
2) Also in JpaConfig in entityManagerFactory(), add the Service class package to the following setPackagesToScan. Previously, the domain object package was there, but the service object package was not. We added "myapp.service", the 2nd package.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaPropertyMap(this.jpaProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
lef.setPackagesToScan("myapp.domain", "myapp.service"); //NOTE: Service was missing
return lef;
}
You have to add #EnableTransactionManagement annotation to JpaConfig class in order to enable Spring's annotation-driven transaction management capability.

Vaadin Spring no boot : Dependency Injection issue

I'm using Vaadin 7, Spring Data JPA 1.9.4.RELEASE, and Vaadin-Spring 1.0.0 and I have some DI problemes.
I choose not to use Spring Boot because it will automatically do too many things that I cannot "see" and I have encountered some problemes that spent me too much time to understand and find the reason, so I prefer no boot.
The probleme that I encounter is that DI works at a root UI but not for a sub-window of the root UI.
RootUI.java
#SpringUI(path = "/")
public class RootUI extends UI {
#Autowired
private EntityManagerFactory entityManagerFactory; // this one works, but I cannot get EntityManager directly
#Autowired
private ClassService classService; // this one works
#Override
protected void init(VaadinRequest request) {
...
PersonForm form = new PersonForm();
CssLayout layout = new CssLayout();
layout.addComponent(form);
Window subWindow = new Window();
subWindow.setContent(layout);
...
}
}
PersonForm.java
public class PersonForm {
#Autowired
private ClassService classService; // this doesnot work,
public PersonForm(ClassService classService) {
classService.findByName();// since the #Autowired dosenot work, I have to pass the one from rootUI.
}
init() {
classService.findByName(); // null exception
}
}
DBConfig.java
#Configuration
#EnableVaadin
#EnableJpaRepositories(basePackages = {"com.example.person.repository"})
#EnableTransactionManagement
public class DBConfig {
#Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/test?autoReconnect=true&useSSL=false");
config.setUsername("root");
config.setPassword("root");
config.setMaximumPoolSize(20);
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setDataSource(dataSource());
factory.setPackagesToScan("com.example.person");
factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
jpaProperties.put("hibernate.hbm2ddl.auto", "update");
factory.setJpaProperties(jpaProperties);
factory.afterPropertiesSet();
return factory.getObject();
}
}
Try to annotate your PersonForm with some Spring annotation like #Component. Or maybe better try to use annotation from vaadin-spring #SpringView.

Spring 4 with JPA (Hibernate implementation): Transaction not working (no transaction in progress)

Ok, this looks to be a repeated question, however, I have been searching for this over 2 days and no success. Below are the configuration details:
Annotated App Config class
#Configuration
#ComponentScan(basePackages = "com.test")
#EnableTransactionManagement(mode=AdviceMode.PROXY, proxyTargetClass=true)
public class AnnotatedAppConfig {
private static final Logger _logger = Logger
.getLogger(AnnotatedAppConfig.class);
#Bean
public DataSource dataSource() {
// C3P0 datasource configuration
final ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(ReaderUtil.getInstance()
.getProperty(IConst.DB_DRIVER));
} catch (PropertyVetoException e) {
_logger.error("Error setting driver class ", e);
}
dataSource.setUser(ReaderUtil.getInstance().getProperty(
IConst.DB_USER));
dataSource.setPassword(ReaderUtil.getInstance().getProperty(
IConst.DB_PASSWORD));
dataSource.setJdbcUrl(ReaderUtil.getInstance().getProperty(
IConst.DB_URL));
_logger.info("Datasource created successfully");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceUnitName("testunit");
entityManagerFactoryBean.setJpaVendorAdapter(createJPAVendorAdapter());
_logger.info("EntityManagerFactory created successfully");
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager txManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory()
.getObject());
transactionManager.setDataSource(dataSource());
_logger.info("Transaction Manager created successfully");
return transactionManager;
}
private HibernateJpaVendorAdapter createJPAVendorAdapter() {
final HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setDatabase(Database.MYSQL);
jpaVendorAdapter.setDatabasePlatform(ReaderUtil.getInstance()
.getProperty(IConst.HIBERNATE_DB_DIALECT));
return jpaVendorAdapter;
}
}
Annotated Service Class
#Service
#Transactional(value="txManager")
public class TestServiceImpl extends BaseServiceImpl implements ITestService {
#Autowired
private ITestDAO testDAO;
#Override
#Transactional(propagation=Propagation.REQUIRES_NEW, readOnly=false, value="txManager")
public Long register(final String username, final String password,
final String name, final String address, final Integer deptId) {
return testDAO.register(username, password, name, address, deptId);
}
}
When ever I try to invoke the register method, then the below error is thrown (in DEBUG mode):
TransactionAspectSupport.completeTransactionAfterThrowing(534) | Completing transaction for [com.test.service.TestServiceImpl.register] after exception: javax.persistence.TransactionRequiredException: no transaction is in progress
07 Jul 2015 18:59:36,488 230371 [http-bio-9080-exec-5]:DEBUG - RuleBasedTransactionAttribute.rollbackOn(131) | Applying rules to determine whether transaction should rollback on javax.persistence.TransactionRequiredException: no transaction is in progress
I have tried all that I could find on the net, however, no luck.
Any experts, please help me know what is missing in the configuration.
PLEASE NOTE: Autowiring is working fine, read transactions (SELECT queries) are working fine.
I read somewhere that #Service & #Transactional annotations do not work if applied together on the same class (which is happening in my case, TestServiceImpl has both the annotations). Is this the case? What would be the workaround in such a situation?

JPA with Spring MVC Configured via Annotations

I am trying to create a Spring MVC application leveraging JPA for its persistence layer. Unfortunately, I was getting a NullPointerException when accessing the EntityManager as Spring does not appear to be injecting it. My configuration is all annotation-based with #EnableWebMvc. After some searching, I added #Transactional on my DAO and #EnableTransactionManagement on my #Configuration class. Then I got an error about not having a DataSource. Supposedly, a class with #EnableTransactionManagement needs to implement TransactionManagementConfigurer. However, I am having problems figuring out how to create the DataSource as well as why it cannot get it from my persistence.xml.
I would greatly appreciate any help in trying to get the EntityManager injected into my DAO.
My #Configuration class
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.example.myapp")
public class MvcConfig extends WebMvcConfigurerAdapter
implements TransactionManagementConfigurer {
private static final boolean CACHE_ENABLED = true;
private static final String TEMPLATE_PATH = "/WEB-INF/freemarker";
private static final String TEMPLATE_SUFFIX = ".ftl";
private static final Logger LOG = Logger.getLogger( MvcConfig.class );
#Override
public void addResourceHandlers( ResourceHandlerRegistry registry ) {
registry.addResourceHandler( "/stylesheets/**" ).addResourceLocations( "/stylesheets/" );
}
#Bean
public FreeMarkerConfigurer configureFreeMarker() {
final FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath( TEMPLATE_PATH );
return configurer;
}
#Bean
public ViewResolver configureViewResolver() {
final FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache( CACHE_ENABLED );
resolver.setSuffix( TEMPLATE_SUFFIX );
return resolver;
}
#Bean
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new DataSourceTransactionManager();
}
}
My DAO
#Component
#Transactional
public class MyDAO {
private static final Logger LOG = Logger.getLogger( MyDAO.class );
#PersistenceContext
private EntityManager entityManager;
public MyClass getMyClass() {
LOG.debug( "getMyClass()" );
final CriteriaQuery<MyClass> query = criteriaBuilder.createQuery( MyClass.class );
// more code here, but it breaks by this point
return myData;
}
}
My Updated Code
I have reached the point in which it almost all works. The EntityManager is being injected properly. However, transactions are not working. I get errors if I try to use a RESOURCE_LOCAL approach so I am looking at JTA managed transactions. When I add #Transactional on any of my DAO methods, I get a "Transaction marked for rollback" error with no further details in any log files to assist troubleshooting. If I remove the annotation from a basic read-only select, the select will work perfectly fine (not sure if I should even be putting the annotation on select-only methods). However, I obviously need this working on methods which perform db writes. If I debug through the code, it seems to retrieve the data perfectly fine. However, as it returns from the method, the javax.transaction.RollbackException gets thrown. From my understanding of everything, it seems as if the exception occurs in the AOP post-method processing.
My #Configuration class
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.example.myapp")
public class MvcConfig extends WebMvcConfigurerAdapter {
private static final boolean CACHE_ENABLED = true;
private static final String TEMPLATE_PATH = "/WEB-INF/freemarker";
private static final String TEMPLATE_SUFFIX = ".ftl";
private static final Logger LOG = Logger.getLogger( MvcConfig.class );
#Override
public void addResourceHandlers( ResourceHandlerRegistry registry ) {
registry.addResourceHandler( "/stylesheets/**" ).addResourceLocations( "/stylesheets/" );
}
#Bean
public FreeMarkerConfigurer configureFreeMarker() {
final FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath( TEMPLATE_PATH );
return configurer;
}
#Bean
public ViewResolver configureViewResolver() {
final FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache( CACHE_ENABLED );
resolver.setSuffix( TEMPLATE_SUFFIX );
return resolver;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JtaTransactionManager();
}
#Bean
public AbstractEntityManagerFactoryBean entityManagerFactoryBean() {
LocalEntityManagerFactoryBean factory = new LocalEntityManagerFactoryBean();
factory.setPersistenceUnitName( "my_db" );
return factory;
}
}
In my application I didn't implement TransactionManagerConfigurer interface. I use next code to configure JPA (with Hibernate implementation). You can do the same in your configuration class.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean factoryBean =
new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] {"com.dimasco.springjpa.domain"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
//vendorAdapter.setGenerateDdl(generateDdl)
factoryBean.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.hbm2ddl.auto", "update");
factoryBean.setJpaProperties(additionalProperties);
return factoryBean;
}
#Bean
public DataSource dataSource() {
final ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(driverClass);
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setMinPoolSize(3);
dataSource.setMaxPoolSize(15);
dataSource.setDebugUnreturnedConnectionStackTraces(true);
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Hope this will help you)
EDIT:
You can get datasource using JNDI lookup:
#Bean
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
More details you can find in this article. There is example with JndiDatasourceConfig class.
EDIT 2:
I ahve persistence.xml in my project, but it is empty:
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="JPA_And_Spring_Test">
</persistence-unit>
</persistence>
And I didn't specify any persistent unit name in my java configuration.
The following might help, even though it uses XML-based configuration:
https://github.com/springinpractice/sip13/blob/master/helpdesk/src/main/resources/spring/beans-repo.xml
It uses Spring Data JPA, but you don't have to do that. Use
#PersistenceContext private EntityManager entityManager;
(But consider Spring Data JPA as it provides very capable DAOs out of the box.)
Side note: For DAOs, favor #Repository over #Component. Both work for component scanning, but #Repository better describes the intended use.

Resources