Here is my Repository configuration:
#Configuration
public class RepositoryConfing {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactoryBean.setPackagesToScan("com.imdb.model");
return entityManagerFactoryBean;
}
#Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/imdb");
ds.setUsername("***");
ds.setPassword("***");
ds.setInitialSize(5);
return ds;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.POSTGRESQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
return adapter;
}
}
When I call merge method, I get an exception: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'merge' call
May be my RepositoryConfig is lacking of some additional configurations?
EDIT:
My new Repository configuration:
#Configuration
#EnableTransactionManagement
public class RepositoryConfing {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter){
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactoryBean.setPackagesToScan("com.imdb.model");
return entityManagerFactoryBean;
}
#Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/imdb");
ds.setUsername("***");
ds.setPassword("***");
ds.setInitialSize(5);
return ds;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.POSTGRESQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
return adapter;
}
#Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
}
Now it fails with StackOverflow. May be DataSourceTransactionManager is not suit for JPA?
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
Your txManager method should be renamed to transactionManager.
You need to have a JpaTransactionManager instead of a DataSourceTransactionManager
EDIT
In order to have fully functional transaction with JPA you need to :
Use #EnableTransactionManagement on your configuration file
Declare an EntityManagerFactoryBean
Declare a JpaTransactionManager (the bean must be named transactionManager) created with your previously declared EntityManagerFactoryBean
Inject (with #PersistenceContext or #Autowired) an EntityManager in your #Repository (which must be a spring bean)
Call your repository from a #Service and use a public method anotated with #Transactional
Of course this is simplified and I assume you are using java config, annotations, and autocomponent scanning.
Related
I am trying to use Crud Repository from Spring-jpa-data:
My config with data access beans looks like:
#Configuration
#EnableTransactionManagement
#ComponentScan (basePackages = {"com.comp.olme"})
#PropertySource("classpath:OlmeSmb-${env}.properties")
#EnableJpaRepositories(basePackages="com.comp.olme", entityManagerFactoryRef ="emGapSort", transactionManagerRef = "txManagerGapSort")
#EnableScheduling
public class OlmeSmbConfig {
#Bean
public BasicDataSource olmeDataSource() {
BasicDataSource olmeDataSource = new BasicDataSource();
...
return olmeDataSource;
}
#Bean
public BasicDataSource gapSortDataSource() {
BasicDataSource gapSortDataSource = new BasicDataSource();
...
return gapSortDataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean emFactoryOLME(#Qualifier("olmeDataSource") BasicDataSource olmeDataSource) {
LocalContainerEntityManagerFactoryBean localConnectionFactoryBean = new LocalContainerEntityManagerFactoryBean();
...
return localConnectionFactoryBean;
}
#Bean
public LocalContainerEntityManagerFactoryBean emFactoryGapSort(#Qualifier("gapSortDataSource") BasicDataSource gapSortDataSource) {
LocalContainerEntityManagerFactoryBean localConnectionFactoryBeanGapSort = new LocalContainerEntityManagerFactoryBean();
...
return localConnectionFactoryBeanGapSort;
}
#Bean
public EntityManager emOLME(#Qualifier("emFactoryOLME") EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public EntityManager emGapSort(#Qualifier("emFactoryGapSort") EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public JpaTransactionManager txManagerOLME(#Qualifier("emFactoryOLME") EntityManagerFactory entityManagerFactoryOLME) {
JpaTransactionManager txManagerOLME = new JpaTransactionManager();
...
return txManagerOLME;
}
#Bean
public JpaTransactionManager txManagerGapSort(#Qualifier("emFactoryGapSort") EntityManagerFactory entityManagerFactoryGapSort) {
JpaTransactionManager txManagerGapSort = new JpaTransactionManager();
....
return txManagerGapSort;
}
}
So, as you can see, i have two datasources, two EntityManagerFactories, two TransactionManagers and others...
But i pass only one EntityManagerFactory into #EnableJpaRepositories annotation (entityManagerFactoryRef ="emGapSort").
The question is: how two use more than one DataSources (entityManagerFactory) with Spring-jpa-data?
I read one example where splitting config described as a solution, but i would like to use one single Spring Config. Is it possibe?
Thank you.
From spring boot - #5401
After a careful consideration we've decided not to implement this. That annotation proposal of yours is a mix of configuration key prefix, bean prefix and other bean-related settings. While it sounds appealing on paper, it would be quite hard to implement and probably even harder to keep it consistent with user's customizations.
So, you may try put it on two #Configuration classes (one #EnableJpaRepositories per #Configuration).
I wore a java based app and used Spring Boot
This is the model :
#Entity
#Table(name = "task_list")
public class Task implements Serializable
And this is the Config class that Spring boot uses it to start :
#Configuration
#EnableAutoConfiguration
#EnableJpaRepositories
#EnableTransactionManagement
#ComponentScan(basePackages = {"controller", "dao", "service"})
class Config {
#Bean(name = "dataSource")
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
factory.setDataSource(dataSource());
return factory;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
And this is The Application :
#SpringBootApplication()
public class Application {
public static void main(String[] args) {
SpringApplication.run(Config.class);
}
}
So When i run the application it works and creates all bean BUT
when i want to interact with Database , Hibernate got this error
org.hibernate.MappingException: Unknown entity: model.Task
I Think this is because of there is not any persistence.xml to mapping
model class,
SO what should i do in Spring boot app ?
where should put that xml?
is there any Annotation that tells to spring boot to map the model classes ?
Thanks in advance .
Thanks to Xtreme Biker , I solve the problem
i just add the model package in LocalContainerEntityManagerFactoryBean.
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("model");
factory.setDataSource(dataSource());
return factory;
}
I am using Spring 4.16. I want to parameterize my persistence data. This is my config right now:
#Configuration
#EnableTransactionManagement
public class PersistenceConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(this.dataSource());
entityManager.setPackagesToScan(new String[] {"com.example.movies.domain"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManager.setJpaVendorAdapter(vendorAdapter);
entityManager.setJpaProperties(this.properties());
return entityManager;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/sarasa_db");
dataSource.setUsername("root");
dataSource.setPassword("mypassword");
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties properties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.setProperty("hibernate.show_sql", "false");
return properties;
}
}
And i want to parameterize on my application.properties all things i can. First of all, i want to put in datasource properties (so as i was reading, is possible that spring builds my datasource automatically, but apparently that is only using JdbcTemplate...):
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sarasa_db
spring.datasource.username=root
spring.datasource.password=mypassword
And, if it's possible, all the Properties's properties, which i couldn't find nothing in doc
Do you know how could i do it ?
EDIT
This is my DAO implementation
#Configuration
#Import(PersistenceConfiguration.class)
public class DAOConfiguration {
#PersistenceContext
private EntityManager entityManager;
#Bean
public ClientDAO clientDAO() {
SimpleJpaRepository<Client, String> support = this.getSimpleJpaRepository(Client.class);
return new MySQLClientDAO(support);
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
#Description("Hibernate repository helper")
protected <T> SimpleJpaRepository<T, String> getSimpleJpaRepository(Class<T> domainClass) {
return new SimpleJpaRepository<T, String>(domainClass, this.entityManager);
}
}
You could do something like this:
First define PropertySourcesPlaceholderConfigurer bean somewhere in your Spring configuration:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("application.properties"));
return ppc;
}
This configuration assumes that application.properties file is placed at the root of your classpath.
After setting up the property placeholder configurer you can access the properties in your database configuration class like so:
#Configuration
#EnableTransactionManagement
public class PersistenceConfiguration {
#Value("${spring.datasource.url}")
private String jdbcUrl;
// ...
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(jdbcUrl);
// ...
}
}
If you want an easy way to parametrize all properties, you should take a look at Spring Boot. It uses the application.properties file to automatically create data source with those properties, and many other things. This is probably the automatic datasource creation you mentioned in your question.
Below is the PersistenceConfig.java file which I use for development. However in production, that would look quite different since I'm using postgresql there. What is a good way to deal with that? So I'm not forced into a if-else hell.
import ...;
#Configuration
#EnableJpaRepositories
public class PersistenceConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setPackagesToScan(packagesToScan);
factory.setJpaVendorAdapter(jpaVendorAdapter());
// factory.setJpaProperties(additionalProperties());
return factory;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
Use Spring Profiles to differentatie between environments. I would configure everything for production and just override for dev. Also move some properties to a properties file.
#Configuration
#EnableJpaRepositories
#PropertySource("classpath:application.properties")
public class PersistenceConfig {
#Autowired
private Environment env;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource dataSource() {
// Production configuration
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource);
factory.setPackagesToScan(packagesToScan);
factory.setJpaVendorAdapter(jpaVendorAdapter());
// factory.setJpaProperties(additionalProperties());
return factory;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(env.getProperty("hibernate.show-sql", Boolean.class, false));
hibernateJpaVendorAdapter.setGenerateDdl(env.getProperty("hibernate.generate-schema", Boolean.class, false));
hibernateJpaVendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect" );
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Configuration
#Profile("dev")
public static class DevPersistenceConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
}
}
Something like that should work. The defaults are configured for production and when the profile dev is active (can be set using the spring.profiles.active property as environment variable) then the datasource will be overridden by the one in the DevPersistenceConfig class.
The dialect can be set using an entry in the application.properties file (or whatever you like to name it).
Why is persistence.xml file needed:
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages="es.japanathome")
public class DataAccessConfig
{
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(Boolean.TRUE);
vendorAdapter.setShowSql(Boolean.TRUE);
factory.setJpaVendorAdapter( vendorAdapter );
factory.setDataSource( dataSource() );
factory.setPackagesToScan("es.japanathome.domain");
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
Spring knows where can find my entities, so I don't understand why is still needed this file.
Depending on which Spring version you use, you may not need to provide a persistence.xml.
Since Spring 3.1, persistence.xml is no longer explicitly required when you're using LocalContainerEntityManagerFactoryBean. Basically packagesToScan was added allowing a way for Spring to find #Entity classes.