Populate a datasource during the start of the spring application - java

I want to run some part of the code to populate the database with dummy data every time the server starts. I use Tomcat as my servlet container. My application is created using Spring. Is there a hook where I can run my code to populate the db just after my application is started?

You have two different alternatives.
The first one is using Spring's DataSourceInitializer. You can pass your initialisation query as a parameter and it executes that query. You can execute any command that you like.
Example:
<bean id="dbInitializer" class="org.springframework.jdbc.datasource.init.DataSourceInitializer">
<property name="dataSource" ref="myDataSourceRef"/>
<property name="enabled" value="true"/>
<property name="databasePopulator">
<bean class="org.springframework.jdbc.datasource.init.ResourceDatabasePopulator">
<property name="continueOnError" value="true"/>
<property name="ignoreFailedDrops" value="true"/>
<property name="sqlScriptEncoding" value="UTF-8"/>
<property name="scripts">
<array>
<value type="org.springframework.core.io.Resource">init.sql</value>
</array>
</property>
</bean>
</property>
</bean>
The second alternative is implementing a Spring ApplicationListener. Populate each datum from that listener to your database manually. It is a little harder to achieve.
Example:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class ApplicationListenerBean implements ApplicationListener {
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ApplicationContext applicationContext = ((ContextRefreshedEvent) event).getApplicationContext();
// now you can do applicationContext.getBean(...)
// ...
}
}
}
This bean must be initialised by Spring. You can either define it in your applicationContext.xml or in your configuration class.
By the way, you can always listen for your ServletContext status by using a ServletContextListener. But if you are using Spring, there are easier methods.

You can use Liquibase, and if you're using Spring Boot all you have to do is add liquibase-core to your classpath, via maven or whatever build tool you're using is. Spring Boot uses YAML files by default. Spring Boot will then run Liquibase on every application startup.

Related

Liquibase with Spring Boot and multiple schemas, how to specify execution order

We have an data-migration job which needs to initialize schemas A and B, in that order. We handle multiple schemas by defining multiple SpringLiquibase, one for each schema, each with its own datasource and its own master changeset. (Note, normally in Spring Boot you wouldn't need to define a SpringLiquibase, because it would detect a single datasource and auto-configure the SpringLiquibase for you with that datasource.)
The execution order seems to vary depending whether the job is run locally within the IDE, or bundled as a single-JAR Spring Boot app.
How can we ensure that the two executions of liquibase happen in the order we want?
(Why the order is important: A contains some tables, while B contains views that reference tables in A. We have to make sure that we grant select on A.* to B before attempting to create view B.some_view (...) as select ... from A.xyz, otherwise the creation of B fails due to insufficient privileges.)
After some scratching of heads and digging into the source code, it turns out to be extremely simple.
SpringLiquibase implements InitializingBean and executes the Liquibase update within the InitializingBean.afterPropertiesSet() method.
Spring calls this method on each bean, one by one, after finishing initializing each one.
So to force a particular order, you need to force the order in which the beans are defined in the Spring context. And the easiest way to do this is with the #DependsOn annotation.
So we put in place something like:
#Bean
public SpringLiquibase liquibaseA(
#Qualifier("dataSourceA") DataSource dataSource,
#Qualifier("liquibasePropertiesA") LiquibaseProperties liquibaseProperties
) {
return instantiateSpringLiquibase(dataSource, liquibaseProperties);
}
#Bean
#DependsOn("liquibaseA")
public SpringLiquibase liquibaseB(
#Qualifier("dataSourceB") DataSource dataSource,
#Qualifier("liquibasePropertiesB") LiquibaseProperties liquibaseProperties
) {
return instantiateSpringLiquibase(dataSource, liquibaseProperties);
}
private SpringLiquibase instantiateSpringLiquibase(DataSource dataSource, LiquibaseProperties liquibaseProperties) {
// set the datasource from dataSource and everything else from liquibaseProperties
}
This is not for spring boot but if you manage your migrations via the change logs this workaround will help. This assumes you have different data sources for different schemas.
<bean id="liquibase1" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource1" />
<property name="changeLog" value="classpath:db1-changelog1.xml" />
</bean>
<bean id="liquibase2" depends-on="liquibase1" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource2" />
<property name="changeLog" value="classpath:db2-changelog1.xml" />
</bean>
<bean id="liquibase3" depends-on="liquibase2" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource1" />
<property name="changeLog" value="classpath:db1-changelog2.xml" />
</bean>
<bean id="liquibase4" depends-on="liquibase3" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource2" />
<property name="changeLog" value="classpath:db2-changelog2.xml" />
</bean>

Creating a datasource from fetching the data source

Currently,I am storing database details in a property file and then creating an datasource using
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>${driverClassName}</value>
</property>
<property name="url">
<value>${url}</value>
</property>
<property name="username">
<value>${username}</value>
</property>
<property name="password">
<value>${password}</value>
</property>
</bean>
My Client asked us to place a config database and this DB will store all the i18keys and the main database values.
So I need to create one two datasources one is for Configs and other is the main database.
I can create the config data sources using the same. But How I can create an second datasource as all the database details are stored in config database.
can you pointers will be very helpful.
You might take a look into the Java-configuration for Spring. You can combine that with your current XML-configuration using <context:component-scan base-package="..."/>.
The general approach would be to configure the first datasource for configuration (like in your current setup) using XML. The XML should also refer to a 'configuration class'.
That is a special class, annotated with #Configuration, which gets the first datasource injected (or maybe some DAO), and then defines a method like so:
#Bean
public DataSource secondDataSource() {
// Construct the second datasource using the configuration
// retrieved from the first datasource.
return new BasicDataSource();
}
Note that you might want to add a qualifier to either (or even both) datasources so you can distinguish between the two datasources when you want to have them injected into other beans using #Injector #Autowired.

How to contribute to a list property of a bean using Spring?

I have 3 projects:
framework
product-a
product-b
Each of the products depends on the framework, but they don't know each other.
I have 3 spring configuration files: one for each project. The configuration file of each product includes (with <import resource="classpath:/...) the configuration file of the framework.
In the framework there is a bean called "manager", which has a property List<AnInterface> theList. The "manager" has a addXxx(anImplementation), which adds elements to the list).
The framework, and each of the product provide implementations of AnInterface, which have to be added to theList.
So in the end, when product-a is running, the manager contains implementations from the framework, and from product-a, idem for product-b
What is the best practice to perform this initialization with Spring ?
The only solution I could think about is to create a dedicated class which contructor will take the manager and a list of contributions, and add them to the manager, but it's ugly because 1/ It manipulate external objects in the constructor, 2/ I have to create a dummy class just to initialize other classes... I don't like that.
I think that code should not know about Spring if it is not really needed. Therefore I would do all initialization in Spring config.
We can use bean definition inheritance and property overriding to do it.
Framework class
public class Manager {
private List<AnInterface> theList;
public void init() {
// here we use list initialized by product
}
}
Framework context
<bean id="manager"
init-method="init"
abstract="true"
class="Manager">
<property name="theList">
<list/> <!-- this will be overriden or extnded -->
</property>
</bean>
Product A context
<bean id="managerA"
parent="manager"
scope="singleton"
lazy-init="false">
<property name="theList">
<list>
<ref bean="impl1"/>
<ref bean="impl2"/>
</list>
</property>
</bean>
Watch out for parent and child properties in such configuration. Not all are inherited from parent. Spring documentation specifies:
The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, scope, lazy init.
Moreover, there is also collection merging in Spring so by specifing in child bean
<list merge="true">
you can merge parent and child lists.
I have observed this pattern in a number of projects and some extendable Web frameworks based on Spring.
I have accepted the answer of Grzegorz because it's a clean solution to my initial problem, but here as an alternate answer, the a technical solution to contribute to a list property of an existing bean.
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="manager"/>
<property name="targetMethod"><value>addXxx</value></property>
<property name="arguments"><list value-type="com.xxx.AnInterface">
<value ref="impl1" />
<value ref="impl2" />
...
</list></property>
</bean>

Linking Spring project to MySQl database

I have been trying to access MySQL routines from my Spring project using SimpleJdbcDaoSupport.
I have a class called 'AdminSimpleMessageManager', which implements the interface 'AdminMessageManager'.
'AdminSimpleMessageManager' has an instance of the class 'AdminSimpleJdbcMessageDao', which implements the interface 'AdminMessageDao'.
AdminSimpleJdbcMessageDao has the following method:
public class AdminSimpleJdbcMessageDao extends SimpleJdbcDaoSupport implements AdminMessageDao {
public int addMessage(String from, String message) {
return getJdbcTemplate().queryForInt("call insert_contact_message(?, ?)", from, message);
}
}
I have included the following in my application context:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/OctagonDB"/>
</bean>
<bean id="adminMessageManager" class="Managers.AdminSimpleMessageManager">
<property name="adminMessageDao" ref="adminMessageDao"/>
</bean>
<bean id="adminMessageDao" class="Managers.dao.AdminSimpleJdbcMessageDao">
<property name="dataSource" ref="dataSource"/>
</bean>
but I feel there are a few important lines missing. I get the error
FAIL - Deployed application at context path /NewWebsite but context failed to start
among other things.
You need to include a MySQL JDBC driver in your classpath. Furthermore you should update the config of the driver class name to com.mysql.jdbc.Driver. org.gjt.mm.mysql.Driver is only retained for backwards compatibility.
However, since you seem to be loading the datasource through JNDI I guess the driver JAR should be in your container (which provides the datasource through JNDI) rather than in your app's WEB-INF/lib?

Programmatically loading Entity classes with JPA 2.0?

With Hibernate you can load your Entity classes as:
sessionFactory = new AnnotationConfiguration()
.addPackage("test.animals")
.addAnnotatedClass(Flight.class)
.addAnnotatedClass(Sky.class)
.addAnnotatedClass(Person.class)
.addAnnotatedClass(Dog.class);
Is there a way to do the same thing - programmatically loading your Entity classes - in a JPA 2.0 compliant way?
The reason for this question is because I'd like to dynamically load my Entity classes, thus not necessarily programmatically.
With the help of Spring I did this in a JPA compliant way.
My "persistence.xml" looks empty, with no entities listed within the <persistence-unit> element.
I then wrote a class that implemented PersistenceUnitPostProcessor like so:
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
public class ReflectionsPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
private String reflectionsRoot;
private Logger log = LoggerFactory.getLogger(ReflectionsPersistenceUnitPostProcessor.class);
#Override
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
Reflections r = new Reflections(this.reflectionsRoot, new TypeAnnotationsScanner());
Set<String> entityClasses = r.getStore().getTypesAnnotatedWith(Entity.class.getName());
Set<String> mappedSuperClasses = r.getStore().getTypesAnnotatedWith(MappedSuperclass.class.getName());
for (String clzz : mappedSuperClasses)
{
pui.addManagedClassName(clzz);
}
for (String clzz : entityClasses)
{
pui.addManagedClassName(clzz);
}
}
public String getReflectionsRoot() {
return reflectionsRoot;
}
public void setReflectionsRoot(String reflectionsRoot) {
this.reflectionsRoot = reflectionsRoot;
}
}
Then I adjusted my spring context xml like this:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</bean>
</property>
<property name="persistenceUnitName" value="GenericPersistenceUnit"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
<property name="persistenceUnitPostProcessors">
<list>
<bean class="com.austinmichael.core.repository.ReflectionsPersistenceUnitPostProcessor">
<property name="reflectionsRoot" value="com.austinmichael"/>
</bean>
</list>
</property>
</bean>
Note the registration of the ReflectionsPersistenceUnitPostProcessor in the persistenceUnitPostProcessors setting.
And that's it. Every class with a JPA Entity or MappedSuperclass annotation on the classpath is added to the classpath. I had to give reflections the prefix of a package name to scan through which is why com.austinmichael is there at all. You could register a second ReflectionsPersistenceUnitPostProcessor with a different package name prefix if you want if your entities don't share a common package name prefix.
But, this is now JPAVendor agnostic.
Is there a way to do the same thing - programmatically loading your Entity classes - in a JPA 2.0 compliant way?
No, this is not supported by JPA so you'll have to do this in a provider specific way. James Sutherland described the process for EclipseLink in this thread like this:
You can access the EclipseLink ServerSession from the EntityManagerFactoryImpl (getServerSession()), and use its' addDescriptor(ClassDescriptor) or addDescriptors() API to add EclipseLink ClassDescriptor. You will need to build the ClassDescriptor meta-data objects directly yourself (or use the Mapping Workbench to create them), as loading from JPA annotations or orm.xml would be more difficult.
Also have a look at this more recent thread for more code sample (the API looks like a bit verbose).
References
Re: [eclipselink-users] Close to get it. Just a little help
Re: JPA: adding entities to EntityManagerFactory programmatically
I don't think there is such option - JPA supports scanning the classpath for entities or explicitly listing the entity classes in the persistence.xml. Since you're using hibernate as the persistence provider however you can always resort to hibernate code. Have a look at the HibernateEntityManager and HibernateEntityManagerFactory classes. You can cast entity manager factories and entity managers to them and do the usual hibernate stuff.

Categories