How to make use of spring declarative transactions along with EntityManagerFactory? - spring

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;
}

Related

Is there a way to stop Spring from rollbacking every transaction in a test method and do a Rollback after it executes?

I'm trying to write some integration tests for an app that I write as a practise. I have a test class with #Transactional annotation on it, to stop tests from committing any changes to db. I've noticed that rollback happens for every method of JPA repository I use. When I save an entity and then try to find a values for mentioned entity I get an empty List since that entity does not exists in DB. So do I have to change my approach or is there a way to wrap all of test method contents into a single transaction that will be rolledback after test method is done running?
Service that is tested:
#Service
public class FridgeServiceImpl implements FridgeService {
private final FridgeRepository fridgeRepository;
public FridgeServiceImpl(FridgeRepository fridgeRepository) {
this.fridgeRepository = fridgeRepository;
}
#Override
public Fridge save(Fridge fridge) {
return fridgeRepository.save(fridge);
}
#Override
public List<IngredientInFridge> getAllInFridge(Long id) {
return fridgeRepository.findAllInFridge(id);
}
#Override
public List<IngredientInFridge> getAllToExpireAfterDate(LocalDate date) {
return fridgeRepository.findAllWithExpirationDateAfter(date);
}
Repository:
#Repository
#Transactional
public interface FridgeRepository extends JpaRepository<Fridge, Long> {
#Query(value = "select i from Fridge f join f.fridgeContents i where i.id.fridgeId = :fridgeId")
List<IngredientInFridge> findAllInFridge(#Param("fridgeId") Long fridgeId);
#Query(value = "select i from IngredientInFridge i inner join IngredientGrammage ig on i.ingredient.id = ig.ingredient.id where i.fridge.id = :fridgeId " +
"and ig.recipe.id = :recipeId")
Set<IngredientInFridge> getIngredientsAvailableForRecipe(#Param("recipeId") Long recipeId, #Param("fridgeId") Long fridgeId);
#Query(value = "select i from IngredientInFridge i where i.expirationDate <= :date")
List<IngredientInFridge> findAllWithExpirationDateAfter(#Param("date") LocalDate date);
Test class:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {TestConfig.class})
#Transactional
class FridgeServiceImplTest {
#Autowired
FridgeService fridgeService;
Ingredient ingredient;
Fridge fridge;
IngredientInFridge ingredientInFridge;
#BeforeEach
public void setUp() {
ingredient = new Ingredient(100, "Chicken", UnitOfMeasure.G);
fridge = new Fridge(new HashSet<>());
ingredientInFridge = new IngredientInFridge(ingredient, fridge, 50, LocalDate.of(2020, 12, 30));
fridge.addToFridge(ingredientInFridge);
}
#Test
void getAllInFridge_ThenReturn_1() {
//given
var savedFridge = fridgeService.save(fridge); //save an entity
//when
var ingredientsInFridge = fridgeService.getAllInFridge(savedFridge.getId()); //empty List since fridgeService.save(fridge) was rolledback.
//then
Assertions.assertEquals(1, savedFridge.getFridgeContents().size());
Assertions.assertEquals(1, ingredientsInFridge.size()); // failed assertion
}
Test config:
#Configuration
#EnableJpaRepositories(value = "com.calculate.calories.repository")
#PropertySource("classpath:applicationTest.properties")
#ComponentScan(basePackages = {"com.calculate.calories.service", "com.calculate.calories.repository"})
#EntityScan("com.calculate.calories.model")
public class TestConfig {
#Autowired
private Environment environment;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.calculate.calories.model");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Objects.requireNonNull(environment.getProperty("spring.datasource.driver-class-name")));
dataSource.setUrl(Objects.requireNonNull(environment.getProperty("spring.datasource.url")));
dataSource.setUsername(Objects.requireNonNull(environment.getProperty("spring.datasource.username")));
dataSource.setPassword(Objects.requireNonNull(environment.getProperty("spring.datasource.password")));
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
//JpaTransactionManager transactionManager = new JpaTransactionManager();
//transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return new DataSourceTransactionManager(dataSource());
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "none");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
I tried to use JpaTransactionManager instead of DataSourceTransactionManager but if I do so, there are no rollbacks for the tests and db is flooded with duplicate data. I've also attempted changing
#Transactional from class level to method level but it changed nothing.
I've seen this post: Does #Transactional annotation on test methods rollback every transaction in the annotated method before it gets to the end?
But when I try to use the EntityManager.flush() after saving it throws javax.persistence.TransactionRequiredException: no transaction is in progress

NoUniqueBeanDefinitionException when setting up multiple JPA datasources

I've followed many tutorials and reviewed many answers (in SO and other sites), and I'm quite confident I didn't miss anything, but I can't get two datasources/EntityManagers to work in my environment.
This is what I have
DbADataSourceConfig.java
package com.myorg.rest.config.dba;
import ...
#Configuration
// #EnableJpaRepositories(
// basePackages = "com.myorg.rest.dao.dbA",
// entityManagerFactoryRef = "dbAEntityManager",
// transactionManagerRef = "dbATransactionManager"
// )
#EnableTransactionManagement
public class DbADataSourceConfig {
#Autowired
private EnvProperties settings;
#Bean
#Primary
public DataSource prjDataSource() {
DataSourceProperties ds = settings.getdbADatasource();
return ds.initializeDataSourceBuilder().type(BasicDataSource.class).build();
}
#Bean(name = "dbAEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean dbAEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(prjDataSource());
em.setPackagesToScan(new String[] { "com.myorg.model.entities.dba" });
em.setPersistenceUnitName("dbAUnit");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
// em.afterPropertiesSet();
return em;
}
#Bean(name = "dbATransactionManager")
#Primary
public PlatformTransactionManager dbATransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(dbAEntityManager().getObject());
return transactionManager;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
DbBDataSourceConfig.java
package com.myorg.rest.config.dbb;
import ...
#Configuration
// #EnableJpaRepositories(
// basePackages = "com.myorg.rest.dao.dbB",
// entityManagerFactoryRef = "dbBEntityManager",
// transactionManagerRef = "dbBTransactionManager"
// )
#EnableTransactionManagement
public class DbBDataSourceConfig {
#Autowired
private EnvProperties settings;
#Bean
#Primary
public DataSource prjDataSource() {
DataSourceProperties ds = settings.getdbBDatasource();
return ds.initializeDataSourceBuilder().type(BasicDataSource.class).build();
}
#Bean(name = "dbBEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean dbBEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(prjDataSource());
em.setPackagesToScan(new String[] { "com.myorg.model.entities.dbb" });
em.setPersistenceUnitName("dbBUnit");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
// em.afterPropertiesSet();
return em;
}
#Bean(name = "dbBTransactionManager")
#Primary
public PlatformTransactionManager dbBTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(dbBEntityManager().getObject());
return transactionManager;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
}
AbstractDBADAO.java
package com.myorg.rest.dao.dba.base;
public abstract class AbstractDBADAO<T extends Serializable> implements IAbstractJpaDAO<T> {
private Class<T> clazz;
#PersistenceContext(unitName = "dbAUnit")
private EntityManager entityManager;
public AbstractDBADAO(Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public T findOne(String id) {
return entityManager.find(clazz, id);
}
public List<T> findAll() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> entityQuery = criteriaBuilder.createQuery(clazz);
entityQuery.from(clazz);
return entityManager.createQuery(entityQuery).getResultList();
}
public void create(T entity) {
entityManager.persist(entity);
}
public T update(T entity) {
return entityManager.merge(entity);
}
public void delete(T entity) {
entityManager.remove(entity);
}
public void deleteById(String entityId) {
T entity = findOne(entityId);
delete(entity);
}
}
and last IAbstractJpaDAO.java
package com.myorg.rest.dao.dba.base;
import ...
public interface IAbstractJpaDAO<T extends Serializable> {
T findOne(String id);
List<T> findAll();
void create(T entity);
T update(T entity);
void delete(T entity);
void deleteById(String entityId);
}
Of course, there's also a bunch of #Repositorys extending IAbstractDAO interface and a corresponding bunch of #Services extending AbstractDAO<T>, but those are just skeletons like
EntityARepo.java
package com.myorg.rest.dao.dba.repositories;
import ...
#Repository
public interface EntityARepo extends IAbstractJpaDAO<EntityA> {
}
EntityAService.java
package com.myorg.rest.dao.dba.services;
import ...
#Service
public class EntityAService extends AbstractDBADAO<EntityA> implements EntityARepo {
public EntityAService() {
super(EntityA.class);
}
}
Similarly, there's a whole bunch of clases set up for the second database (dbB).
The problem
No matter what I do, I get the following problem (or something very similar):
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1285)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)
From what I understand, the #PersistenceContext can not resolve to the correct LocalContainerEntityManagerFactoryBean. They both have setPersistenceUnitName('dbXUnit'), so they should be able to get resolved correctly, right?.
I do not have any persistence.xml file, I'm doing everything programmatically.
What I've tried so far
Obviously, the code above.
Also, you will notice there's a commented section for #EnableJpaRepositories; that doesn't work either.
I've also tried to build a PersistenceUnitManager for each of the DataSource config classes to no success.
I've tried #Autowired, with and without the #PersistenceContext, no luck either.
I've tried #PersistenceContext on a setter and using #Qualifier on the input EntityManager, no luck either.
I've tried with and without the #Primary decorators.
Please keep in mind any inconsistencies between the files above posted are surely a copy/paste error, I've had to edit names in order to keep confidentiality. Also note that the code I have compiles, runs, and doesn't output any other errors except for the one listed above. Also, with only one of those Database Config classes set up, the project runs as expected, and I can execute the tests on that connection.
The question
Can anyone spot why the #PersistenceContext is not wiring the
correct dependency and complains of a duplicate? or
Should I do things in a completely different manner?
You are going the correct way so far.
You need 2 of each:
EntityManager/factory
DataSource
TransactionManager
JPARepository-Configs
The key to success is IMO the correct and unique naming of them all (2x4=8 different names). #Primary and #Secondary do not help here.

problem with writing to 2 databases from Spring boot

I was trying this code from github:https://github.com/kodinor/spring-data-many-dbs
It's an example of how to use multiple db's from a Spring-boot application. it worked fine but I added the Spring-boot-starter-web dependency and now I'm getting an error:
>Method requestMappingHandlerMapping in >org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$En>ableWebMvcConfiguration required a single bean, but 2 were found:
> - productDSEmFactory: defined by method 'productDSEmFactory' in class >path resource [com/kodinor/configuration/ProductDBConfiguration.class]
> - userDSEmFactory: defined by method 'userDSEmFactory' in class path >resource [com/kodinor/configuration/UserDBConfiguration.class]
>
>
>Action:
>
>Consider marking one of the beans as #Primary, updating the consumer to >accept multiple beans, or using #Qualifier to identify the bean that should >be consumed<br>
I have two config files:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackageClasses = UserRepository.class, entityManagerFactoryRef = "userDSEmFactory", transactionManagerRef = "userDSTransactionManager")
public class UserDBConfiguration {
#Primary
#Bean
#ConfigurationProperties("spring.datasource1")
public DataSourceProperties userDSProperties() {
return new DataSourceProperties();
}
#Primary
#Bean
public DataSource userDS(#Qualifier("userDSProperties") DataSourceProperties userDSProperties) {
return userDSProperties.initializeDataSourceBuilder().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean userDSEmFactory(#Qualifier("userDS") DataSource userDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(userDS).packages(User.class).build();
}
#Primary
#Bean
public PlatformTransactionManager userDSTransactionManager(EntityManagerFactory userDSEmFactory) {
return new JpaTransactionManager(userDSEmFactory);
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackageClasses = ProductRepository.class, entityManagerFactoryRef = "productDSEmFactory", transactionManagerRef = "productDSTransactionManager")
public class ProductDBConfiguration {
#Bean
#ConfigurationProperties("spring.datasource2")
public DataSourceProperties productDSProperties() {
return new DataSourceProperties();
}
#Bean
public DataSource productDS(#Qualifier("productDSProperties") DataSourceProperties productDSProperties) {
return productDSProperties.initializeDataSourceBuilder().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean productDSEmFactory(#Qualifier("productDS") DataSource productDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(productDS).packages(Product.class).build();
}
#Bean
public PlatformTransactionManager productDSTransactionManager(EntityManagerFactory productDSEmFactory) {
return new JpaTransactionManager(productDSEmFactory);
}
}
Application.properties:
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.datasource1.url=jdbc:mysql://localhost:3306/userdb?autoReconnect=true&useSSL=false
spring.datasource1.username=user
spring.datasource1.password=pass
spring.datasource2.url=jdbc:mysql://localhost:3306/productdb?autoReconnect=true&useSSL=false
spring.datasource2.username=user
spring.datasource2.password=pass
And a simple main app that adds some init data:
#SpringBootApplication
public class SpringDataManyDbsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDataManyDbsApplication.class, args);
}
#Autowired
private UserRepository userRepository;
#Autowired
private ProductRepository productRepository;
#PostConstruct
void init () {
User user = new User("john", "doe");
userRepository.save(user);
Product product = new Product("chair", 5);
productRepository.save(product);
}
}
I tried to add the #Primary annotation to userDSEmFactory. The error goes away but the product data isn't saved anymore. Any ideas how to save this problem? I don't have a lot of experience with Spring-boot and I've read dozens of articles but many seem to do things in a different way. Thanks so much for helping me out!
update
I've added the #Primary annotation like this:
#Primary
#Bean
public LocalContainerEntityManagerFactoryBean userDSEmFactory(#Qualifier("userDS") DataSource userDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(userDS).packages(User.class).build();
}
No there are no more errors, but only the user is being saved in the primary db and nothing is being saved to the product (second) database.
So if anybody has suggestions what could be the cause of that..
You are missing #Primary annotation on your userDSEmFactory
#Primary
#Bean
public LocalContainerEntityManagerFactoryBean userDSEmFactory(#Qualifier("userDS") DataSource userDS, EntityManagerFactoryBuilder builder) {
return builder.dataSource(userDS).packages(User.class).build();
}
And also it is goot to use once #EnableTransactionManagement at your main class.

Spring Data REST, cannot save, can select

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) { ... }
}

Spring jdbc configuration

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
}

Resources