Spring jdbc configuration - spring

I have been trying to implement a web service using spring. This webservice will provide data access to a mySQL database using JDBC. I am trying to not use any xml configuration files, so I have come across a problem trying to connect to the database.
I am following the tutorial: http://spring.io/guides/tutorials/rest/ but I changed a few things along the way.
Now that I am trying to implement the connection with the database I get an error when trying to execute the tomcat instance, and I guess the problem is within the configurations.
Here follows some of my code:
Datasource configuration:
#Configuration
#Profile("mySQL")
#PropertySource("classpath:/services.properties")
public class MySQLDataSourceConfiguration implements DataSourceConfiguration{
#Inject
private Environment environment;
#Bean
public DataSource dataSource() throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setPassword(environment.getProperty("dataSource.password"));
dataSource.setUrl(environment.getProperty("dataSource.url"));
dataSource.setUsername(environment.getProperty("dataSource.user"));
dataSource.setDriverClassName(environment.getPropertyAsClass("dataSource.driverClass", Driver.class).getName());
return dataSource;
}
}
the file service.properties is where I keep my configurations for the database, so when I desire to change the database I will just have to change 4 fields.
The JDBCConfiguration class for the setup of the JDBCtemplate
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSourceConfiguration.dataSource());
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
Then there is the Repository, that recieves the template.
#Transactional
#Repository
#Qualifier("jdbcRepository")
public class JdbcIndividualRepository implements IndividualsRepository{
private static final Logger LOG = LoggerFactory.getLogger(JdbcIndividualRepository.class);
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
public JdbcIndividualRepository(DataSource jdbcDataSource) {
LOG.info("JDBCRepo arg constructor");
this.jdbcTemplate = new JdbcTemplate(jdbcDataSource);
}
#Override
public Individual save(Individual save) {
String sql = "INSERT INTO Individual(idIndividual, Name) VALUES(?,?)";
this.jdbcTemplate.update(sql, save.getId(), save.getName());
return save;
}
#Override
public void delete(String key) {
String sql = "DELETE FROM Individual WHERE idIndividual=?";
jdbcTemplate.update(sql, key);
}
#Override
public Individual findById(String key) {
String sql = "SELECT i.* FROM Individual i WHERE i.idIndividual=?";
return this.jdbcTemplate.queryForObject(sql, new IndividualRowMapper(), key);
}
#Override
public List<Individual> findAll() {
String sql = "SELECT * FROM Individual";
return new LinkedList<Individual>(this.jdbcTemplate.query(sql, new IndividualRowMapper()));
}
}
Then I register the jdbc configuration in the initializer class when creating the root context of the application as follows:
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(CoreConfig.class, SecurityConfig.class, JdbcConfiguration.class);
rootContext.refresh();
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
However, the Tomcat server wont run because it can't autowire the class MySQLDataSourceConfiguration.
Anyone knows what the problem might be? I can give more details on the code, but the question is already really large.
Appreciate any kind of help!
Cheers
EDIT
Solved changing the JdbcConfiguration class to:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public IndividualsRepository createRepo(){
return new JdbcIndividualRepository(dataSource);
}
}

Remove
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
Because that's not how it's supposed to be used. Instead add to the same class the following:
#Autowired DataSource dataSource;
and use it like this: new JdbcTemplate(dataSource);
Also, try adding #ComponentScan to JdbcConfiguration class. From what I see in your code the class JdbcIndividualRepository is not picked up by anything.

In your class JdbcConfiguration, you are trying to autowire DataSourceConfiguration. I'm not really sure if that's possible - typically you should try to autwire the DataSource, not the DataSourceConfiguration.
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
Also if you have several DataSources and you're using Spring profiles to separate them, it's easier to provide all the DataSource beans in one file and annotate each bean with a different profile:
#Configuration
public class DataSourceConfig {
#Bean
#Profile("Test")
public DataSource devDataSource() {
.... configure data source
}
#Bean
#Profile("Prod")
public DataSource prodDataSource() {
... configure data source
}

Related

Can we insert data into tables without entity in spring boot?

I have a large amount of data, so can I insert it into the database without creating an Entity class?
you can use JdbcTemplate to insert, update, delete and more.
Using Java Config it would be:
#Configuration
public class DBConfig {
#Bean
public DataSource dataSource() {
//create a data source
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
#Bean
public TransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
you should create a repository that uses JdbcTemplate could be:
#Repository
public class JdbcSomeRepository implements SomeRepository {
private final JdbcTemplate jdbcTemplate ;
#Autowired
public JdbcSomeRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
#Transactional
public int someUpdate(SomeType someValue, SomeOtherType someOtherValue) {
return jdbcTemplate.update("INSERT INTO SomeTable(column1, column2) VALUES(?,?)", someValue, someOtherValue)
}
}
you can also directly Autowired in the controller.
#Autowired
JdbcTemplate jdbcTemplate ;

#Primary datasource not picked up in testing

I want to test only the persistence layer using #DataJpaTest, and I have two datasource configurations, one in src/main and the other is src/test, and I am using #primary on test datasource to get picked up only, but main datasource get picked up also.
src/main configuration
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.****.repository",
entityManagerFactoryRef = "platformEntityManagerFactory",
transactionManagerRef = "platformTransactionManager"
)
#Import(CommonPersistenceConfig.class)
public class PlatformPersistenceConfig {
#Value("classpath:application.yml")
private Resource resource;
#Bean
#Qualifier("platformTransactionManager")
public PlatformTransactionManager platformTransactionManager(#Qualifier("platformEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
// code...
}
#Bean
public LocalContainerEntityManagerFactoryBean platformEntityManagerFactory(DataSource dataSource,
HibernateProperties hibernateProperties) {
// code...
}
#Bean
public DataSource getDataSource() throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
// code...
}
}
src/test configuration
#Configuration
#Import({CommonPersistenceConfig.class, DbPropertyConfig.class})
public class DbTestSetupConfig {
#Autowired
private TestDatasourceProperties dbProperties;
#Primary
#Bean(destroyMethod = "close")
public DataSource getDataSource() throws Exception {
// code...
}
#PostConstruct
public void dbSetup() throws Exception {
// code...
}
}
my test
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class StripeCardRepositoryTest {
#Autowired
StripeCardRepository stripeCardRepository;
#Test
public void test() {
}
}
The point of #AutoConfigureTestDatabase is to replace an existing datasource defined in src/main/.. with a datasource defined in src/test. But to do so you must inform it what strategy it must use. You have chosen AutoConfigureTestDatabase.Replace.NONE therefore it does not replace the main datasource with the one that you need.
Try to switch to AutoConfigureTestDatabase.Replace.ANY and it will correctly replace it with the one defined in test.
Also probably you show this in the documentation
In the case of multiple DataSource beans, only the #Primary
DataSource is considered.
This applies in the case that you have multiple datasources in project defined. It means that it would replace only the primary datasource with the strategy defined. It does not mean that it would pick only the primary and ignore the others.

How to use Multiple JdbcOperations and Multiple JdbcTemplates in Spring

I have 2 different datasrouces from which I want to use in the same file and query each of them using JdbcOperations implementation. Is this possible?
#Repository
public class TestRepository {
private JdbcOperations jdbcOperations;
#Inject
#Qualifier("dataSource1")
private DataSource dataSource1;
#Inject
#Qualifier("dataSource2")
private DataSource dataSource2;
#Bean
#Qualifier("jdbcTemplate1")
public JdbcTemplate jdbcTemplate1(#Qualifier("dataSource1") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
#Qualifier("jdbcTemplate2")
public JdbcTemplate jdbcTemplate1(#Qualifier("dataSource2") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Inject
public TestRepository(JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations; //HOW DO I SPECIFY WHICH JDBCTEMPLATE SHOULD BE USED FOR INITIALIZING THIS JDBCOPERATIONS
}
}
Above is my code, note that JdbcOperations is initialized in the constructor. But no way to specify which jdbcTemplate should the jdbcOperations use.
The qualifier should actually be put at the parameter level:
public TestRepository(#Qualifier("jdbcTemplate2")JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations;
}
Uses the bean named jdbcTemplate2

#Transactional on Spring shutdown to properly shutdown Hsqldb

The heart of this question is: Is it possible to execute a Transaction from a method triggered by a Spring shutdown hook?
At the moment I have a HyperSqlDbServer class that implements SmartLifeCycle as found in this question:
In a spring bean is it possible to have a shutdown method which can use transactions?
I have a method in that class that is marked transactional that gets invoked as part of the stop method:
#Transactional
public void executeShutdown() {
hsqlDBShutdownService.executeShutdownQuery();
hsqlDBShutdownService.closeEntityManager();
}
The service used in that method is a bit of a hack that I had to do because I could not autowire in the EntityManager to this class:
#Service
public class HsqlDBShutdownService {
#PersistenceContext
private EntityManager entityManager;
#Autowired
private HyperSqlDbServer hyperSqlDbServer;
#Transactional
public void executeShutdownQuery() {
entityManager.createNativeQuery("SHUTDOWN").executeUpdate();
}
#Transactional
public void closeEntityManager() {
entityManager.close();
}
#PostConstruct
public void setHsqlDBShutdownService() {
hyperSqlDbServer.setShutdownService(this);
}
}
You may notice that all I'm really trying to accomplish is invoking the query "SHUTDOWN" before stopping the server. Without this, the hsqldb lock file sticks around on server restart, and the server throws an exception.
The code above produces the following exception:
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:96)
...
So my original question stands, but if anyone has a thought on how I could execute this query another way I'll try that as well.
FYI, I've also tried the #PreDestroy annotation, but get the same TransactionRequiredException.
Edit: For completeness, I am using the JpaTransactionManager and the #Transactional annotations work throughout my project, except on shutdown...
Edit 2: Datasource and transaction manager configuration:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:persistence.properties")
public class PersistenceConfig implements TransactionManagementConfigurer {
private static final String PASSWORD_PROPERTY = "dataSource.password";
private static final String USERNAME_PROPERTY = "dataSource.username";
private static final String URL_PROPERTY = "dataSource.url";
private static final String DRIVER_CLASS_NAME_PROPERTY = "dataSource.driverClassName";
#Autowired
private Environment env;
#Bean
#DependsOn("hsqlDb")
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty(DRIVER_CLASS_NAME_PROPERTY));
dataSource.setUrl(env.getProperty(URL_PROPERTY));
dataSource.setUsername(env.getProperty(USERNAME_PROPERTY));
dataSource.setPassword(env.getProperty(PASSWORD_PROPERTY));
return dataSource;
}
#Bean
#DependsOn("hsqlDb")
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("com.mycompany.model.db");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, env.getProperty(org.hibernate.cfg.Environment.DIALECT));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_AUTO));
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, env.getProperty(org.hibernate.cfg.Environment.SHOW_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Override
#Bean()
#DependsOn("hsqlDb")
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new JpaTransactionManager();
}
}
I found a workaround for shutting down the HsqlDB database, but it involves avoiding the use of Spring's EntityManager and #Transactional as they apparently do not work during server shutdown. My modified HsqlDBShutdownService is below. The key change is that instead of using the EntityManager to invoke the query, I create a new jdbc connection manually, and invoke the query that way. This avoids the requirement for #Transactional:
#Service
public class HsqlDBShutdownService {
#Autowired
private ApplicationContext applicationContext;
#PersistenceContext
private EntityManager entityManager;
#Autowired
private HyperSqlDbServer hyperSqlDbServer;
public void executeShutdownQuery() {
Connection conn = null;
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(this.applicationContext.getBean(DataSource.class));
conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
conn.setAutoCommit(true);
jdbcTemplate.execute("SHUTDOWN");
} catch(Exception ex) {
ex.printStackTrace();
} finally {
try {
if(conn != null)
conn.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
#Transactional
public void closeEntityManager() {
entityManager.close();
}
#PostConstruct
public void setHsqlDBShutdownService() {
hyperSqlDbServer.setShutdownService(this);
}
}
The server can now restart successfully without leaving Hsqldb lock files around.

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