How to mention persistenceUnitName when packagesToScan property - java

I have two datasources and two entityManagerFactory instance. I was trying to use the new feature of 3.1 (Bootstrapping JPA entityManagerFactory without persistence.xml by using packagesToScan property).
In order to use the right entity manager factory instance, i have to distinguish using Persistence unit name and defining the PU name in persistence.xml is stopping the spring package scanning feature.
How to give the PU name while using packagesToScan feature?
My question is more duplicate of Is there a way to give persistenceUnitName for Spring's LocalContainerEntityManagerFactoryBean without persistence.xml?
I couldn't find the answer or comment on the above post. So reposting as new question.

Yes you can. Here's an example that uses annotation configuration for Spring
I found it best to organise each datasource into a different package.
My package structure is:
datasource
|__ converters <-- holds any custom attribute converters for JPA
|__ default <-- for default datasource
| |__ model <-- contains entities for default datasource
| |__ repository <-- contains repositories for default datasource
|__ anotherdatasource <-- for second datasource
|__ model <-- contains entities for second datasource
|__ repository <-- contains repositories for second datasource
Pick one of the datasources as the default and create a configuration class for it along the lines of...
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = { "com.example.datasource.default.repository" })
public class JpaDefaultDatasourceConfig {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.example.datasource.default.model", "com.example.datasource.converters").persistenceUnit("mydefault").build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(#Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Then for each subsequent datasource create another configuration class along the lines of... (Note: the use of packages to separate entity/repository scanning and the naming convention used throughout)
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "anotherEntityManagerFactory", transactionManagerRef = "anotherTransactionManager", basePackages = { "com.example.datasource.anotherdatasource.repository" })
public class JpaAnotherDatasourceConfig {
#Bean(name = "anotherDataSource")
#ConfigurationProperties(prefix = "another.datasource")
public DataSource anotherDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "anotherEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean anotherEntityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier("anotherDataSource") DataSource anotherDataSource) {
return builder.dataSource(anotherDataSource).packages("com.example.datasource.anotherdatasource.model", "com.example.datasource.converters").persistenceUnit("anotherName").build();
}
#Bean(name = "anotherTransactionManager")
public PlatformTransactionManager anotherTransactionManager(#Qualifier("anotherEntityManagerFactory") EntityManagerFactory anotherEntityManagerFactory) {
return new JpaTransactionManager(anotherEntityManagerFactory);
}
}
You can configure each datasource using the configuration prefix. For the two examples above you could configure them using
application.yml
## JPA configuration
# This is the configuration for default datasource created by spring
spring:
datasource:
url: jdbc:mysql://localhost/default
username: foo
password: bar
driverClassName: com.mysql.jdbc.Driver
test-on-borrow: true
test-while-idle: true
validation-query: select 1;
# maxActive: 1
# This is the configuration for an additional datasource which we will create ourselves
another:
datasource:
url: jdbc:mysql://localhost/another
username: foo
password: bar
driverClassName: com.mysql.jdbc.Driver
test-on-borrow: true
test-while-idle: true
validation-query: select 1;
You don't necessarily need to worry now about the names of the persistence units (although we did name them) because we've carefully separated entity managers to only look at their entities/repositories you can simply inject the applicable repository and use it without having to worry about it getting the wrong datasource. If you do need the persistence unit you can just ask for it by name #PersistenceUnit(name = "anotherDatasource")

If I understand your question correctly, you would like to set the name of the persistenceUnit backing an EntityManagerFactory, when defined without a persistence.xml?
When you declare the entityManagerFactory, there is a persistenceUnitName property that you can set. For example:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="yourPersistenceUnitName"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="packagesToScan">
<list>
<value>..</value>
...
</list>
</property>
</bean>

Related

Combing xml and java configuration for spring/hibernate datasource as AWS RDS

Trying to connect my spring/hibernate project which is running locally on my machine to my aws rds.I am trying to modify my previous setup which was a h2 local database datasource. Unsure how to combine the xml and java config to import the bean into the java config?
My dataconfig.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
xmlns:jdbc="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cloud/aws/context
http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd">
<aws-context:context-region region="eu-west-1a"/>
<aws-context:context-credentials>
<aws-context:simple-credentials access-key="
my key" secret-key="my key" />
</aws-context:context-credentials>
<jdbc:data-source
db-instance-identifier="mydb"
password="my password">
</jdbc:data-source>
Dataconfig.java
#Configuration
#PropertySource("app.properties")
#EnableJpaRepositories(basePackages = "haughton.icecreamapi.dao")
public class DataConfig {
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(env.getProperty("entity.package"));
factory.setJpaProperties(getHibernateProperties());
return factory;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
properties.put("hibernate.implicit_naming_strategy",env.getProperty("hibernate.implicit_naming_strategy"));
properties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
//removed to keep db consistent
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
return properties;
}
#Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
// Driver class name
ds.setDriverClassName(env.getProperty("db.driver"));
// Set URL
ds.setUrl(env.getProperty("db.url"));
// Set username & password
ds.setUsername(env.getProperty("db.username"));
ds.setPassword(env.getProperty("db.password"));
return ds;
}
}
SPRING CLOUD / AWS
The Spring Cloud doc Section 3.1.2 tells you how to move your XML config into annotations. It says to add some annotations to your Application Configuration object. (Create a new one if you don't have one yet.)
#Configuration
#EnableContextInstanceData
public static class ApplicationConfiguration {
}
You have some options for specifying your keys:
use the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
use the system properties aws.accessKeyId and aws.secretKey
use the user specific profile credentials file. To do this, just run aws configure from whatever user is running your application and set the keys and region there. If you go this route, remember to do this in production as part of your system setup.
use the instance profile credentials (you aren't running on an EC2 instance, so this won't work for you)
I recommend going with the 3rd bullet point option.
You may also be able to wire up the values from your application.properties file, but I don't know how to do that.
DATA SOURCE
Source: https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-one-configuration/
#Configuration
class PersistenceContext {
#Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
HikariConfig dataSourceConfig = new HikariConfig();
dataSourceConfig.setDriverClassName(env.getRequiredProperty("db.driver"));
dataSourceConfig.setJdbcUrl(env.getRequiredProperty("db.url"));
dataSourceConfig.setUsername(env.getRequiredProperty("db.username"));
dataSourceConfig.setPassword(env.getRequiredProperty("db.password"));
return new HikariDataSource(dataSourceConfig);
}
//Add the other beans here
}
This example uses the Hikari connection pooling framework. IF you want to use that, you'll have to add the appropriate dependency.
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.4</version>
</dependency>
That requires Java 7; there are other compatibility versions available for Java 6-9. Of course, you can use different connection pool if you wish.

how to add if else condtion in hibernate configuration file

I am working in spring hibernate project where I am connecting to multiple database. These database details are loaded in a properties file and is imported into hibernate.xml file. Each key was mapped as below.
dbName= dbHost
Mapping as below :
<bean id="dbId" parent="abstractDataSource">
<property name="url" value="${dbName}" />
</bean>
But now i need to include a condition such that the property key to be mapped to multiple values like below
E.g. dbName= statusFlag,dbHost,dbBackupHostName
I need to modify the hibernate configuration file such that using the keyName, corresponding values should be retrieved and splitted with comma separated.
Then I need to check for the statusFlag. If it is true, then i should set the dbValue to 'dbHost', else if the flag is false, it should be set to 'dbBackupHostName'.
Please help me how to implement this condition check in hibernate configuration file.
Also please let me know if this approach is fine to proceed or am i making it too difficult to implement?
Waiting for some valueable answers..
~~~Suriya
To use complex logic in bean wiring and creation in spring you can use Java Configuration
It should work for Spring 3. Sample pseudo code is below.
#Configuration
public class MyCustomConfig {
#Value
String statusFlag;
#Value
String dbHost;
#Value
String dbBackupHostName;
#Bean
public BasicDataSource datasource () {
BasicDataSource datasource = new BasicDataSource();
//Do some custom processing to create datasource
...
return datasource;//return after creation
}
}
Use PropertyPlaceholderConfigurer to load properties as #PropertySource is not available in 3.0.5.
If you can change the spring version to 3.1 which shouldn't make much difference then you can use #Profile along with #PropertySource.
If the logic is not too complex and you can separate set of properties that have to be active at a time. (Local db, Dev db, Prod db, Custom etc.) Then you can try using #Profile.
#Profile annotated bean is created only if that profile is active. A profile can be activated by setting spring.profiles.active.
So to activate the Dev profile we can set in the properties files
spring.profiles.active=dev
//activating 2 profiles
spring.profiles.active=dev,mvc
Below is the sample pseudo code.
#Configuration
#PropertySource("bootstrap.properties")//set spring.profiles.active in it
public class MyCustomConfig {
#Profile("profile1")
public BasicDataSource datasource1 () {
//config using ${profile1.dbName} etc
...
}
#Profile("profile2")
public BasicDataSource datasource2 () {
//config using ${profile2.dbName} etc
...
}
#Profile("profile3")
public BasicDataSource datasource3 () {
//config using ${profile3.dbName} etc
...
}
}

how to convert jndi lookup from xml to java config

Currently I'm converting the xml to java config. But I stuck at some part that I have been research for several days. Here the problem:
Xml config:
<jee:jndi-lookup id="dbDataSource" jndi-name="${db.jndi}" resource-ref="true" />
<beans:bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate" >
<beans:property name="dataSource" ref="dbDataSource"></beans:property>
</beans:bean>
So far I managed to convert this code:
<jee:jndi-lookup id="dbDataSource" jndi-name="${db.jndi}" resource-ref="true" />
to this :
#Bean(name = "dbDataSource")
public JndiObjectFactoryBean dataSource() {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("${db.jndi}");
bean.setResourceRef(true);
return bean;
}
And this :
<beans:bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate" >
<beans:property name="dataSource" ref="dbDataSource"></beans:property>
</beans:bean>
to this:
#Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
return jt;
}
The problem is the method setDataSource() need DataSource object but I'm not sure how to relate both bean.How to pass the JndiObjectFactoryBean to DataSource?
Or do I need to use another method?
Extra Question:
The bean.setJndiName("${db.jndi}") , ${db.jndi} is refer to properties file but I always got NameNotFoundException, How to make it work?
Thanks!!
Instead of JndiObjectFactoryBean use a JndiDataSourceLookup instead. To use the ${db.jndi} in the method declare a method argument and annotate it with #Value.
#Bean(name = "dbDataSource")
public DataSource dataSource(#Value("${db.jndi}") String jndiName) {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiName);
}
Autowired methods and constructors can also use the #Value annotation. -- Spring Reference Guide.
#Bean methods are basically factory methods which are also auto wired methods and as such fall into this category.
In your factory method for the JdbcTemplate you can simply use a DataSource method argument to get a reference to the datasource (If you have multiple you can use the #Qualifier on the method argument to specify which one you want to use).
#Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}

Replace Quartz Spring configuration xml and properties with #Configuration

I'm migrating a web application built with Spring 3 to a Spring 4 Boot based app.
I'd like to replace all xml and properties with a #Configuration class.
Original spring xml config
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:quartz.properties"/>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
</bean>
Original quartz.properties
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.dataSource=psqldatasource
org.quartz.dataSource.psqldatasource.driver=${db.driver}
org.quartz.dataSource.psqldatasource.URL=${db.url}
org.quartz.dataSource.psqldatasource.user=${db.usr}
org.quartz.dataSource.psqldatasource.password=${db.pwd}
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
I've managed to replace spring xml with the following code, but it still use an external quartz.properties to configure most of the quartz features, including datasource.
#Bean
public Scheduler configureScheduler() throws SchedulerException {
StdSchedulerFactory f = new StdSchedulerFactory();
f.initialize(this.getClass().getClassLoader().getResourceAsStream("quartz.properties"));
return f.getScheduler();
}
Note that this leads to the following output during app boot :
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.
I've managed to achieve a full Java Spring configuration using the following code :
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.myapp")
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean configureScheduler() {
SchedulerFactoryBean f = new SchedulerFactoryBean();
f.setDataSource(dataSource);
f.setJobFactory(new SpringBeanJobFactory());
f.setAutoStartup(false);
Properties properties = new Properties();
properties.setProperty("org.quartz.threadPool.threadCount", "3");
properties.setProperty("org.quartz.jobStore.useProperties", "false");
properties.setProperty("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
f.setQuartzProperties(properties);
f.setApplicationContext(applicationContext);
f.setApplicationContextSchedulerContextKey("applicationContext");
return f;
}
}
This produce the following output during app boot
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.springframework.scheduling.quartz.LocalDataSourceJobStore' - which supports persistence. and is not clustered.
To my purpose this is satisfying ; although i'm not sure with the different job-store implementation (was JobStoreTX and now LocalDataSourceJobStore). I'm wondering this is related to the use of the spring autowired datasource.

Is there a way to give persistenceUnitName for Spring's LocalContainerEntityManagerFactoryBean without persistence.xml?

I have multiple datasources and multiple EntityManagerFactories to be defined in my spring context.
If there is only on EntityManagerFactory we don't need to give persistenceUnitName. But to associate EntityManager to correct EntityManagerFactory with #PersistenceContext I should give unitName="somePU" attribute.
But if we give name for persistenceUnitName attribute then Spring is expecting these persistenceUnitName declarations in persistence.xml file.
Is there a way to give persistenceUnitName for Spring's LocalContainerEntityManagerFactoryBean without persistence.xml?
Also I found another issue when I used persistence.xml with Spring's LocalContainerEntityManagerFactoryBean. The packagesToScan property is not working. I need to list down all the Entity classes in persistence.xml.
Any idea why it is happening?
-siva
Specifying property in LocalContainerEntityManagerFactoryBean works for me
<property name="persistenceUnitName" value="MyPersistenceUnit"/>
After creating the LocalEntityManagerFactoryBean, set the persistence unit name:
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("NAME_OF_PERSISTENCE_UNIT");

Categories