Commit addc6a67 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

java code uses flyway to manage migration scripts

parent e74860da
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>lcsb.mapviewer</groupId>
......@@ -75,8 +76,8 @@
<dependency>
<groupId>org.sbml.jsbml</groupId>
<artifactId>jsbml</artifactId>
<version>1.2</version>
<!-- for now we use lower version of log4j and this one introduce some
<version>1.3.1</version>
<!-- for now we use lower version of log4j and this one introduce some
problem with tomcat loggin -->
<exclusions>
<exclusion>
......@@ -91,6 +92,10 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
......
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>lcsb.mapviewer</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>persist</artifactId>
<name>MapViewer persist</name>
<description>Data Access Interface to the model</description>
<dependencies>
<!-- dependency from the MapViewer model -->
<!-- https://mvnrepository.com/artifact/org.flywaydb/flyway-core -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
<!-- dependency from the MapViewer model -->
<dependency>
<groupId>lcsb.mapviewer</groupId>
<artifactId>model</artifactId>
<version>1.0</version>
</dependency>
<!-- apache ftp -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<!-- apache ftp -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<!-- Hibernate -->
<dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<version>${hibernate.version}</version>
<exclusions>
<exclusion>
<artifactId>xml-apis</artifactId>
<groupId>xml-apis</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- Library excluded from above; it was in conflict with Xerces -->
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>${xml-apis.version}</version>
</dependency>
<!-- Log4J -->
</dependency>
<!-- Library excluded from above; it was in conflict with Xerces -->
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>${xml-apis.version}</version>
</dependency>
<!-- Log4J -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Spring -->
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
......@@ -84,28 +90,28 @@
</dependency>
<!-- spring module used for conneting orm (hibernate in our case) -->
<!-- spring module used for conneting orm (hibernate in our case) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- spring module used for password encoding -->
<!-- spring module used for password encoding -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${springframework.security.version}</version>
</dependency>
<!-- spring module used for testing -->
<!-- spring module used for testing -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- postgres connector -->
<!-- postgres connector -->
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
......@@ -122,16 +128,16 @@
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
</dependency>
<!-- Gson -->
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package lcsb.mapviewer.persist;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.flywaydb.core.Flyway;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.ScriptException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class CustomDatabasePopulator implements DatabasePopulator {
Logger logger = Logger.getLogger(CustomDatabasePopulator.class);
private DataSource dataSource;
// public CustomDatabasePopulator(DataSource source) {
// logger.debug("Constructor");
// logger.debug(source);
// }
@Override
public void populate(Connection connection) throws SQLException, ScriptException {
logger.debug(dataSource);
ComboPooledDataSource source = (ComboPooledDataSource) dataSource;
String url = source.getJdbcUrl();
String user = source.getUser();
String password = source.getPassword();
Flyway flyway = new Flyway();
flyway.setDataSource(url, user, password);
flyway.baseline();
flyway.migrate();
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
......@@ -9,12 +9,17 @@ import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.log4j.Logger;
import org.flywaydb.core.Flyway;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.jmx.StatisticsService;
import org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.springframework.beans.factory.annotation.Autowired;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import lcsb.mapviewer.common.exception.InvalidStateException;
/**
......@@ -33,275 +38,275 @@ import lcsb.mapviewer.common.exception.InvalidStateException;
*/
@SuppressWarnings("deprecation")
public class DbUtils extends Observable {
/**
* Default class logger.
*/
private static Logger logger = Logger.getLogger(DbUtils.class);
/**
* Hibernate session factory.
*/
@Autowired
private SessionFactory sessionFactory;
/**
* Default class logger.
*/
private static Logger logger = Logger.getLogger(DbUtils.class);
/**
* Hibernate session factory.
*/
@Autowired
private SessionFactory sessionFactory;
/**
* Service used for connection statistics.
*/
private StatisticsService statisticsService;
/**
* Service used for connection statistics.
*/
private StatisticsService statisticsService;
/**
* This flag indicates if the object was initialized or not.
*
* @see #init()
*/
private boolean initialized = false;
/**
* Bean used for statistics.
*/
private MBeanServer mbeanServer;
/**
* This flag indicates if the object was initialized or not.
*
* @see #init()
*/
private boolean initialized = false;
/**
* Static map containing opened custom sessions. This object is also used for
* synchronization between threads when accessing informations about sessions.
*/
private static Map<Long, Session> sessionForThread = new HashMap<Long, Session>();
/**
* Bean used for statistics.
*/
private MBeanServer mbeanServer;
/**
* This determines if every add/update/delete operation should be flushed (for
* specific {@link Thread}). There are few drawbacks of this approach:
* <ol>
* <li>when autoflushing is set to false, then the data consistency could be
* broken (even in the same thread/transaction)</li>,
* <li>we have to automatically take care of the flushing, therefore there
* might be some problems with long transactions.</li>
* </ol>
*/
private static Map<Long, Boolean> autoFlushForThread = new HashMap<>();
/**
* Static map containing opened custom sessions. This object is also used for
* synchronization between threads when accessing informations about sessions.
*/
private static Map<Long, Session> sessionForThread = new HashMap<Long, Session>();
/**
* This method initialize services responsible for statistics.
*/
private void init() {
if (!initialized) {
if (SpringApplicationContext.getApplicationContext().containsBean("mbeanServer")) {
logger.info("Hibernate statistics turned on");
mbeanServer = SpringApplicationContext.getApplicationContext().getBean(MBeanServer.class);
statisticsService = new StatisticsService();
statisticsService.setSessionFactory(sessionFactory);
statisticsService.setStatisticsEnabled(true);
ObjectName objectName;
try {
objectName = new ObjectName("org.hibernate:name=HibernateStatistics");
/**
* This determines if every add/update/delete operation should be flushed (for
* specific {@link Thread}). There are few drawbacks of this approach:
* <ol>
* <li>when autoflushing is set to false, then the data consistency could be
* broken (even in the same thread/transaction)</li>,
* <li>we have to automatically take care of the flushing, therefore there might
* be some problems with long transactions.</li>
* </ol>
*/
private static Map<Long, Boolean> autoFlushForThread = new HashMap<>();
mbeanServer.registerMBean(statisticsService, objectName);
} catch (Exception e) {
logger.error(e, e);
}
} else {
logger.info("Hibernate statistics turned off");
}
}
initialized = true;
/**
* This method initialize services responsible for statistics.
*/
private void init() {
if (!initialized) {
if (SpringApplicationContext.getApplicationContext().containsBean("mbeanServer")) {
logger.info("Hibernate statistics turned on");
mbeanServer = SpringApplicationContext.getApplicationContext().getBean(MBeanServer.class);
statisticsService = new StatisticsService();
statisticsService.setSessionFactory(sessionFactory);
statisticsService.setStatisticsEnabled(true);
ObjectName objectName;
try {
objectName = new ObjectName("org.hibernate:name=HibernateStatistics");
}
mbeanServer.registerMBean(statisticsService, objectName);
} catch (Exception e) {
logger.error(e, e);
}
} else {
logger.info("Hibernate statistics turned off");
}
}
initialized = true;
/**
* Executes sql query given in the parameter.
*
* @param query
* sql query to be executed
* @return The number of entities updated or deleted.
*/
public int executeSqlQuery(String query) {
return executeSqlQuery(query, new HashMap<String, Object>());
}
}
/**
* Executes sql query given in the parameter.
*
* @param query
* sql query to be executed
* @param params
* map of parameters used in the query
* @return The number of entities updated or deleted.
*/
public int executeSqlQuery(String query, Map<String, Object> params) {
SQLQuery sqlQuery = getSessionFactory().getCurrentSession().createSQLQuery(query);
/**
* Executes sql query given in the parameter.
*
* @param query
* sql query to be executed
* @return The number of entities updated or deleted.
*/
public int executeSqlQuery(String query) {
return executeSqlQuery(query, new HashMap<String, Object>());
}
for (Entry<String, Object> entry : params.entrySet()) {
sqlQuery.setParameter(entry.getKey(), entry.getValue());
}
return sqlQuery.executeUpdate();
}
/**
* Executes sql query given in the parameter.
*
* @param query
* sql query to be executed
* @param params
* map of parameters used in the query
* @return The number of entities updated or deleted.
*/
public int executeSqlQuery(String query, Map<String, Object> params) {
SQLQuery sqlQuery = getSessionFactory().getCurrentSession().createSQLQuery(query);
/**
* Returns hibernate session for current thread.
*
* @return session for the current thread
*/
public Session getSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
}
if (session == null) {
session = sessionFactory.getCurrentSession();
}
return session;
}
for (Entry<String, Object> entry : params.entrySet()) {
sqlQuery.setParameter(entry.getKey(), entry.getValue());
}
return sqlQuery.executeUpdate();
}
/**
* Closes custom session for current thread.
*/
public void closeSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
sessionForThread.remove(id);
}
if (session != null) {
logger.debug("Closing session for thread: " + id);
session.getTransaction().commit();
/**
* Returns hibernate session for current thread.
*
* @return session for the current thread
*/
public Session getSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
}
if (session == null) {
session = sessionFactory.getCurrentSession();
}
return session;
}
session.close();
int counter = -1;
synchronized (sessionForThread) {
sessionForThread.remove(id);
counter = sessionForThread.size();
}
synchronized (autoFlushForThread) {
autoFlushForThread.remove(id);
}
setChanged();
notifyObservers(counter);
logger.debug("Session closed for thread: " + id);
}
}
/**
* Closes custom session for current thread.
*/
public void closeSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
sessionForThread.remove(id);
}
if (session != null) {
logger.debug("Closing session for thread: " + id);
session.getTransaction().commit();
/**
* Creates custom session for current thread.
*/
public void createSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
if (!initialized) {
init();
}
}
session.close();
int counter = -1;
synchronized (sessionForThread) {
sessionForThread.remove(id);
counter = sessionForThread.size();
}
synchronized (autoFlushForThread) {
autoFlushForThread.remove(id);
}
setChanged();
notifyObservers(counter);
logger.debug("Session closed for thread: " + id);
}
}
// we cannot create two threads for one session
if (session != null) {
throw new InvalidStateException("Current thread already has an active session");
} else {
logger.debug("Creating session for thread: " + id);
session = sessionFactory.openSession();
logger.debug("Session opened: " + id);
session.beginTransaction();
logger.debug("Session started: " + id);
if (statisticsService != null) {
logger.debug("Opened sessions: " + statisticsService.getSessionOpenCount() + ". Closed sessions: " + statisticsService.getSessionCloseCount());
}
Integer counter = -1;
synchronized (sessionForThread) {
sessionForThread.put(id, session);
counter = sessionForThread.size();
}
synchronized (autoFlushForThread) {
autoFlushForThread.put(id, true);
}
setChanged();
notifyObservers(counter);
}
}
/**
* Creates custom session for current thread.
*/
public void createSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
if (!initialized) {
init();
}
}
/**
* Returns true if custom session was opened for this thread.
*
* @return <code>true</code> if custom session was created for this thread,
* <code>false</code> otherwise
*/
public boolean isCustomSessionForCurrentThread() {
Long id = Thread.currentThread().getId();
Session session = null;
synchronized (sessionForThread) {
session = sessionForThread.get(id);
}
return session != null;
// we cannot create two threads for one session
if (session != null) {
throw new InvalidStateException("Current thread already has an active session");