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;
}
Related
I'm trying to solve a problem with two datasources. I read many text in the internet and made simple solution based on this source
I've made two "config classes", like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "ResultEntityManagerFactory",
transactionManagerRef = "ResultEntityTransactionManager",
basePackages = "com.migr.result.repositories"
)
public class ResultTablesConfig {
#Autowired
#Qualifier("orclAdapter")
JpaVendorAdapter jpaVendorAdapter;
#Bean(name="ResultEntityDataSource")
#ConfigurationProperties(prefix = "datasource.migr.result")
public DataSource declReaconDS() {
return new DataSource();
}
#Bean(name = "ResultEntityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
#Bean(name = "ResultEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(declReaconDS());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.migr.result.tables.*");
lef.setPersistenceUnitName("ResultEntityPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
#Bean(name = "ResultEntityTransactionManager")
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
}
and another one, practically same - SourceTablesConfig.
only difference is #Bean names (they all start with "Source") and repositories mapping, like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "SourceEntityManagerFactory",
transactionManagerRef = "SourceEntityTransactionManager",
basePackages = "com.migr.source.repositories"
)
public class SourceTablesConfig {
...............
#Bean(name = "SourceEntityManagerFactory")
public EntityManagerFactory entityManagerFactory(){
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(declReaconDS());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.migr.source.tables.*");
lef.setPersistenceUnitName("SourceEntityPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
My services are simle:
#Service
public class CatalogsSourceService {
#Autowired
CatalogsSourceRepository catalogsrepository;
.......
}
where CatalogsSourceRepository extends JpaRepository<Catalogs, Long>
My "main" class:
#RestController
#RequestMapping("/")
public class Main {
#Autowired
CatalogsSourceService source_serv;
#Autowired
CatalogsResultService result_serv;
#RequestMapping
public Object index() {
Map<String,List> results = new HashMap<>();
results.put("First", source_serv.findAll());
results.put("Second", source_serv.findAll());
return results;
}
it worked perfectly when I was having only one DataSource,
but now it exits with error:
expected single matching bean but found 2: SourceEntityManagerFactory,ResultEntityManagerFactory
I don't get why it is happening. My solution looks practically like mentioned in above link, but his do not get such error.
I will be very gratefull for any help
PS. forgot about my "AppConfig":
#Configuration
#ComponentScan(basePackages = { "com.migr" })
public class AppConfig {
#Bean(name="orclAdapter")
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setDatabase(Database.ORACLE);
return jpaVendorAdapter;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Sorry guys, my problem is solved. I missed easiest solution).
I just had to annotate one EntityManagerFactory bean with #Primary annotation.
#Bean(name = "SourceEntityManagerFactory")
#Primary
public EntityManagerFactory entityManagerFactory() {
...........
}
obvious solutions are not always so obvious...
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.
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.