I'm puzzled as to why my Spring Data REST code was working, but not anymore. Probably a configuration issue, but I can't point my finger to what. When I use AJAX to post to a endpoint, I get a 500 error - java.lang.IllegalArgumentException: argument type mismatch. When I try repository.save() from independent java, I get no response. On the other hand, when I perform a get to my API or call repository.find(), I can see Hibernate showing selects happening. So I know that my database is connected. Additionally, I'm using Flyway and those scripts are able to insert without issue.
I'm using Spring-Boot-Starter for Data-REST, Data-JPA, and thymeleaf, all Version 1.2.1.RELEASE. I recognize that this could be a version issue, but I'm keeping it here to eventually integrate with Spring-Security-Kerberos, which I know works with that version of Spring-Boot. At least, I haven't gotten it to work with a later version of Spring-Boot.
Some things I have noticed that may or may not be related:
#Transactional on top my #RestRepositoryResource makes no difference
When calling repository.save(), the returned entity's ID stays at 0 (I'm Auto generation on my entities)
The problem exists on all my entities, including ones without any relations
It didn't matter that I removed unique=true from one of my entities
One of my Repository Interfaces
#RepositoryRestResource(collectionResourceRel = "shifts", path = "shifts")
public interface ShiftRepository extends PagingAndSortingRepository<Shift, Long> {
List<Shift> findBySiteId(#Param("siteId") String siteId);
Shift findByShiftCode(#Param("shiftCode") String shiftCode);
}
My Repository Config Component
#Component
public class RepositoryConfigDev {
#Value("${spring.datasource.driver-class-name}")
private String driverClassName;
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.username}")
private String username;
#Value("${spring.datasource.password}")
private String password;
#Bean
EventHandler eventHandler() {
return new EventHandler();
}
#Bean
DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.SQL_SERVER);
adapter.setDatabasePlatform("org.hibernate.dialect.SQLServer2012Dialect");
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
return adapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setJpaVendorAdapter(jpaVendorAdapter());
emfb.setDataSource(dataSource());
emfb.setPackagesToScan("package.model");
emfb.afterPropertiesSet();
return emfb;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
txManager.setRollbackOnCommitFailure(true);
return txManager;
}
}
So after rebuilding from scratch, I figured out what I did wrong. The problem was in the #RepositoryEventHandler. In that I had originally declared the class as
#RepositoryEventHandler(value={Class1.class, Class2.class})
public class RepoEvents {
....
}
When it should have been
#RepositoryEventHandler
public class RepoEvents {
#HandleBeforeCreate(Class1.class)
public void beforeCreate1(Class1 c) { ... }
#HandleBeforeCreate(Class2.class)
public void beforeCreate2(Class2 c) { ... }
}
Related
I have configured a Spring Batch app to use two different datasources in each step. Only the step that has the datasource annotation #Primary works and the other one errors out since it tries to use that datasource and not the one passed in to the reader/writer.
As soon as I switch the #primary annotation, the opposite step fails.
SO essentially I can only get one step to work - which ever one I put #Primary on.
I thought that I could just get rid of the annotation since I am passing the datasources explicitly to the reader/writer but I get an error stating that #Primary is required...
Any suggestions?
here is the datasource config class where I am pulling in values from my yaml file:
#Bean
#Primary
#ConfigurationProperties("spring.batch.datasource")
public DataSource dataSourceOne(CredentialsFactory credentialsFactory, ResourceLoader resourceLoader) {
ConjurSecret conjurSecret = credentialsFactory.getCredentials(dataSourceOneCredentialAlias);
return DataSourceBuilder.create(resourceLoader.getClassLoader()).username(conjurSecret.getUserName()).password(conjurSecret.getPassword()).url(dataSourceOnedbUrl).build();
}
#Bean
#ConfigurationProperties("spring.ds2.datasource")
public DataSource dataSourceTwoDataSource(CredentialsFactory credentialsFactory, ResourceLoader resourceLoader) {
ConjurSecret conjurSecret = credentialsFactory.getCredentials(dataSourceTwoCredentialAlias);
return DataSourceBuilder.create(resourceLoader.getClassLoader()).username(conjurSecret.getUserName()).password(conjurSecret.getPassword()).url(dataSourceTwoUrl).build();
}
}
And then In my main config class I pass each datasource to each reader/writer accordingly
#Autowired
public DataSource dataSourceOne;
#Autowired
public DataSource dataSourceTwo;
private String table1ReadQuery = "SELECT * FROM table1 a WHERE a.TYPE_CD='TESTX' with ur";
private String table1DeleteQuery = "DELETE FROM table1 WHERE TD_ID=:TdId";
private String table2ReadQuery = "SELECT * FROM table2 a WHERE a.CTGY_CD='TESTX' with ur";
private String tableDeleteQuery = "DELETE FROM table2 WHERE D_ID=:TdId";
#Bean
public Job importUserJob(Step readCurrentAssessment) {
return jobBuilderFactory.get("importUserJob")
.listener(jobListener())
.start(readTableOne)
// .next(readTableTwo)
.build();
}
#Bean
public Step readTableOne(ItemReader<Table1> table1Reader, ItemWriter<Table1> table1Writer){
return stepBuilderFactory.get("table 1")
.listener(stepExecutionListener())
.<Table1,Table1>chunk(2000)
.reader(table1Reader)
.writer(table1Writer)
.build();
}
#Bean
public Step readTableTwo(ItemReader<Table2> table2Reader, ItemWriter<Table2> table2Writer){
return stepBuilderFactory.get("table 2")
.listener(stepExecutionListener())
.<Table2,Table2>chunk(2000)
.reader(table2Reader)
.writer(table2Writer)
.build();
}
#Bean
public JdbcCursorItemReader<Table1> table1Reader(DataSource dataSourceOne) {
return new JdbcCursorItemReaderBuilder<Table1>()
.dataSource(dataSourceOne)
.name("Table1Reader")
.sql(table1ReadQuery)
.rowMapper(new Table1Mapper())
.build();
}
/
#Bean
public JdbcCursorItemReader<Table2> table2Reader(DataSource dataSourceTwo) {
return new JdbcCursorItemReaderBuilder<Table2>()
.dataSource(dataSourceTwo)
.name("Table2Reader")
.sql(readTable2Query)
.rowMapper(new Table2Mapper())
.build();
}
#Bean
ItemWriter<Table2> table2Writer() {
return new JdbcBatchItemWriterBuilder<Table2>()
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql(table2DeleteQuery)
.assertUpdates(true)
.dataSource(dataSourceTwo)
.build();
}
#Bean
ItemWriter<Table1> table1Writer() {
return new JdbcBatchItemWriterBuilder<Table1>()
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.sql(table1Writer)
.assertUpdates(true)
.dataSource(dataSourceOne)
.build();
}
The Only way
I have defined the following configuration in my application
#Bean
#Primary
#ConfigurationProperties(prefix="database1")
public DataSource rameshDS()
{
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "rameshEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean rameshEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(rameshDS())
.packages("com.nandagiri.entities")
.build();
}
#Primary
#Bean(name = "rameshTransactionManager")
public PlatformTransactionManager rameshTransactionManager(
final #Qualifier("rameshEntityManagerFactory") LocalContainerEntityManagerFactoryBean rameshEntityManagerFactory) {
return new JpaTransactionManager(rameshEntityManagerFactory.getObject());
}
If I try to insert data into one of the tables in the following way, the data is not persisted. But if I uncomment the lines to explicitly begin/commit transactions then it is working fine. But, I wanted to use declarative way of transactions. How to achieve that?
#Autowired
#Qualifier("rameshEntityManagerFactory")
EntityManagerFactory rameshEntity;
#Override
#Transactional(value = "rameshTransactionManager")
public void storeInfo(Ramesh ramesh)
{
EntityManager em = rameshEntity.createEntityManager();
//em.getTransaction().begin();
em.persist(ramesh);
//em.getTransaction().commit();
}
If I persist the entities with repository interface it is working absolutely fine without any issues. please find the code below.
#Repository
public interface RameshRepository extends JpaRepository<Ramesh, String>
{
}
#Transactional(transactionManager = "rameshTransactionManager", propagation = Propagation.REQUIRED)
public void saveRameshs()
{
saveRamesh1();
saveRamesh2();
}
#Transactional
public void saveRamesh1()
{
Ramesh ramesh = new Ramesh();
ramesh.setId("8");
ramesh.setFname("jagadeesh");
ramesh.setLname("K");
repository.save(ramesh);
}
#Transactional
public void saveRamesh2()
{
Ramesh ramesh = new Ramesh();
ramesh.setId("9");
ramesh.setFname("jagadeesh123");
ramesh.setLname("k123");
repository.save(ramesh);
//int x = 5/0;
}
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?
I am using spring-boot 1.2.2 with hibernate.version:4.3.6.Final for a simple operation and was using the #Converter for mapping java8 LocalDateTime field to timestamp.
In my converter class, I used autoApply=true as below.
#Converter(autoApply = true)
public class LocalDateTimePersistenceConverter implements
AttributeConverter {
#Override
public java.sql.Timestamp convertToDatabaseColumn(LocalDateTime entityValue) {
return Timestamp.valueOf(entityValue);
}
#Override
public LocalDateTime convertToEntityAttribute(java.sql.Timestamp databaseValue) {
return databaseValue.toLocalDateTime();
}
}
However, I still have to use the #Convert on my entity.
The converter class is part of the packages I scan.
Is it something that I have to do to get this to work automatically without using the #Convert on all DB entries?
::Additionally::
Here is my DB Config
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter());
lef.setPackagesToScan("path to domain and Converter class");
lef.afterPropertiesSet();
return lef;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.ORACLE);
adapter.setShowSql(false);
adapter.setGenerateDdl(false);
return adapter;
}
The only thing I can see is you may need to change this line below
public class LocalDateTimePersistenceConverter implements
AttributeConverter<java.sql.Timestamp, LocaleDateTime>
Therefore, Spring would know how to automatically convert which type of attributes.
The order is incorrect, it should be:
public class LocalDateTimePersistenceConverter implements
AttributeConverter<LocaleDateTime, java.sql.Timestamp>
As the Javadoc states:
javax.persistence.AttributeConverter<X, Y>
Parameters:
X the type of the entity attribute
Y the type of the database column
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.