Skip to content

Commit

Permalink
ARTIF-703 Auto install DDL if the tables don't exist
Browse files Browse the repository at this point in the history
  • Loading branch information
brmeyer committed Jun 12, 2015
1 parent 9a87089 commit 6dd6d07
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 58 deletions.
Expand Up @@ -101,7 +101,6 @@ protected String getErraiModuleId() {
@Override
protected void preConfig() {
System.setProperty("hibernate.show_sql", "true");
System.setProperty("hibernate.hbm2ddl.auto", "create-drop");
System.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
System.setProperty("hibernate.connection.url", "jdbc:h2:mem:dbHibernateTest;DB_CLOSE_DELAY=-1;MVCC=true");
System.setProperty("hibernate.connection.username", "sa");
Expand Down
11 changes: 11 additions & 0 deletions distro/assembly/src/main/assembly/dist.xml
Expand Up @@ -190,5 +190,16 @@
<directoryMode>0755</directoryMode>
<fileMode>0755</fileMode>
</fileSet>
<!-- DDL -->
<fileSet>
<directory>${basedir}/../../repository/hibernate/src/main/resources/ddl</directory>
<outputDirectory>ddl</outputDirectory>
<filtered>true</filtered>
<includes>
<include>*.sql</include>
</includes>
<directoryMode>0755</directoryMode>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
</assembly>
30 changes: 15 additions & 15 deletions doc/guide/en-US/GuideGettingStarted.asciidoc
Expand Up @@ -73,38 +73,38 @@ Feel free to manually create a realm for scratch. However, there are a few requ
3. To use our themed login page, use *artificer* for the *loginTheme*
4. Each user must have at least *admin* and *user* values for the *realmRole*
Startup
~~~~~~~

Once Artificer is installed on your preferred platform, you should be able to go ahead and start it up.

Wildfly & EAP (standalone-full is required)
....
bin/standalone.sh -c standalone-full.xml
....

DDL
~~~

Artificer uses RDBMS (through JPA) for all persistence. The distribution ships with DDL SQL files for the "big 5"
databases: Postgres, MySQL, Oracle, SQL Server, and DB2/IBM. In addition, H2 is included (the default DB
in Wildfly/EAP).

By default, Artificer installs a simple, file-based H2 datasource
During installation, a simple, file-based H2 datasource is automatically deployed.
(see `$JBOSS_HOME/standalone/deployments/artificer-h2-ds.xml`). If you use something other than that datasource, be sure to update the
relevant connection properties in `artificer.properties`. See the "Artificer Server Configuration" section for more details.

For now, you'll need to manually import the DDL, using your favorite SQL client. We're fans of http://www.squirrelsql.org/[SQuirreL]).
To install the DDL, you have two options. Obviously, manually importing it, using your favorite SQL client, is great.
We're fans of http://www.squirrelsql.org/[SQuirreL]).
As an example, if you install SQuirreL and its H2 plugin, then start Wildfly/EAP with Artificer installed,
you could connect to its H2 datasource with the following connection URL. Use it to import the DDL SQL.

....
jdbc:h2:[JBOSS_HOME]/standalone/data/h2/artificer;mvcc=true
....

Yes, using Hibernate's Schema Exporter might be possible, but it's not recommended for production environments.
Further, we maintain and *optimize* the DDL by-hand, as well as utilize an SQL update strategy (Liquibase) for migrations
between Artificer versions. So, reliance on the DDL SQL is vital.
Alternatively, Artificer will automatically install the DDL for you. On startup, it checks to see if the necessary
tables exist. If not, it will automatically execute the DDL SQL for the DB you've selected in `artificer.properties`.

Startup
~~~~~~~

Once Artificer is installed and configured on your preferred platform, you should be able to go ahead and start it up.

Wildfly & EAP (standalone-full is required)
....
bin/standalone.sh -c standalone-full.xml
....

Check your Installation
~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
Expand Up @@ -17,6 +17,7 @@

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.io.IOUtils;
import org.artificer.common.ArtificerConfig;
import org.artificer.common.ArtificerException;
import org.artificer.common.error.ArtificerNotFoundException;
Expand All @@ -26,14 +27,20 @@
import org.artificer.repository.hibernate.entity.ArtificerStoredQuery;
import org.hibernate.Hibernate;
import org.hibernate.ejb.HibernatePersistence;
import org.hibernate.engine.spi.SessionImplementor;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.io.PrintWriter;
import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

/**
* @author Brett Meyer.
Expand Down Expand Up @@ -88,43 +95,112 @@ public T execute() throws ArtificerException {
protected abstract T doExecute(EntityManager entityManager) throws Exception;
}

private static EntityManager entityManager() {
private static EntityManager entityManager() throws Exception {
if (entityManagerFactory == null) {
// Pass in all hibernate.* settings from artificer.properties
Map<String, Object> properties = ArtificerConfig.getConfigProperties("hibernate");

// If a connection is used, we *cannot* rely on Hibernate's built-in connection pool. Instead,
// automatically set up HikariCP.
if (properties.containsKey("hibernate.connection.url")) {
String connectionUrl = (String) properties.remove("hibernate.connection.url");
String username = (String) properties.remove("hibernate.connection.username");
String password = (String) properties.remove("hibernate.connection.password");

HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(connectionUrl);
hikariConfig.setUsername(username);
hikariConfig.setPassword(password);

// In case we're using MySQL, these settings are recommended by HikariCP:
hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
hikariConfig.addDataSourceProperty("useServerPrepStmts", "true");

String dialect = (String) properties.get("hibernate.dialect");
if (dialect != null && dialect.contains("PostgreSQL")) {
// The JDBC jar verion in the IP BOM does not support Connection.isValid(), so need to use this:
hikariConfig.setConnectionTestQuery("SELECT 1");
// If a connection is used, we *cannot* rely on Hibernate's built-in connection pool. Instead,
// automatically set up HikariCP.
initHikariCP(properties);
}

entityManagerFactory = new HibernatePersistence().createEntityManagerFactory(persistenceUnit, properties);

EntityManager entityManager = entityManagerFactory.createEntityManager();
initDDL(entityManager, properties);
return entityManager;
} else {
return entityManagerFactory.createEntityManager();
}
}

private static void initHikariCP(Map<String, Object> properties) {
String connectionUrl = (String) properties.remove("hibernate.connection.url");
String username = (String) properties.remove("hibernate.connection.username");
String password = (String) properties.remove("hibernate.connection.password");

HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(connectionUrl);
hikariConfig.setUsername(username);
hikariConfig.setPassword(password);

// In case we're using MySQL, these settings are recommended by HikariCP:
hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
hikariConfig.addDataSourceProperty("useServerPrepStmts", "true");

String dialect = (String) properties.get("hibernate.dialect");
if (dialect != null && dialect.contains("PostgreSQL")) {
// The JDBC jar verion in the IP BOM does not support Connection.isValid(), so need to use this:
hikariConfig.setConnectionTestQuery("SELECT 1");
}

HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);

properties.put("hibernate.connection.datasource", hikariDataSource);
}

private static void initDDL(EntityManager entityManager, Map<String, Object> properties) throws Exception {
// If the DDL is not already installed in the DB, automatically do it on first use.
SessionImplementor session = (SessionImplementor) entityManager.getDelegate();
Connection connection = session.connection();

if (!hasTables(connection)) {
// our tables don't exist -- create them
String dialect = (String) properties.get("hibernate.dialect");
if (dialect != null) {
String ddlFile;
if (dialect.contains("PostgreSQL")) {
ddlFile = "postgres9.sql";
} else if (dialect.contains("MySQL")) {
ddlFile = "mysql5.sql";
} else if (dialect.contains("Oracle")) {
ddlFile = "oracle10.sql";
} else if (dialect.contains("SQLServer")) {
ddlFile = "mssql2012.sql";
} else if (dialect.contains("DB2")) {
ddlFile = "db2.sql";
} else {
ddlFile = "h2.sql";
}

HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
Statement statement = null;
try {
URL url = HibernateUtil.class.getClassLoader().getResource("ddl/" + ddlFile);
String ddl = IOUtils.toString(url);

properties.put("hibernate.connection.datasource", hikariDataSource);
statement = connection.createStatement();
statement.executeUpdate(ddl);
} finally {
if (statement != null) {
statement.close();
}
// do *not* close the connection -- it will still be used by this instance of the EntityManager
}
}
}
}

entityManagerFactory = new HibernatePersistence().createEntityManagerFactory(persistenceUnit, properties);
private static boolean hasTables(Connection connection) throws Exception {
DatabaseMetaData metadata = connection.getMetaData();

// check if "ArtificerArtifact" table exists
ResultSet tables = metadata.getTables(null, null, ArtificerArtifact.class.getSimpleName(), null);
if (tables.next()) {
return true;
}
return entityManagerFactory.createEntityManager();

// also need to check all caps (thanks, Oracle)
tables = metadata.getTables(null, null, ArtificerArtifact.class.getSimpleName().toUpperCase(Locale.ROOT), null);
if (tables.next()) {
return true;
}

// otherwise, nope
return false;
}

public static ArtificerArtifact getArtifact(String uuid, EntityManager entityManager, boolean fullFetch) throws ArtificerException {
Expand Down
Expand Up @@ -39,6 +39,13 @@ public HibernateRepositoryTestProvider(Map<String, String> extraProperties) {
public void before() throws Exception {
HibernateUtil.setPersistenceUnit("ArtificerTest");

System.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
System.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
System.setProperty("hibernate.connection.url", "jdbc:h2:mem:dbHibernateTest;DB_CLOSE_DELAY=-1;MVCC=true");
System.setProperty("hibernate.connection.username", "sa");
System.setProperty("hibernate.cache.use_second_level_cache", "false");
System.setProperty("hibernate.cache.use_query_cache", "false");
System.setProperty("hibernate.search.default.directory_provider", "ram");
if (extraProperties != null) {
for (String key : extraProperties.keySet()) {
String value = extraProperties.get(key);
Expand Down
13 changes: 2 additions & 11 deletions repository/test/src/test/resources/META-INF/persistence.xml
Expand Up @@ -19,21 +19,12 @@
<exclude-unlisted-classes />

<properties>
<!-- A few items that should probably *not* be changed -->
<property name="hibernate.order_updates" value="true"/>
<property name="hibernate.connection.autocommit" value="false"/>
<!--<property name="hibernate.show_sql" value="true"/>-->
<property name="hibernate.format_sql" value="true"/>

<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.url" value="jdbc:h2:mem:dbHibernateTest;DB_CLOSE_DELAY=-1;MVCC=true"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.cache.use_second_level_cache" value="false"/>
<property name="hibernate.cache.use_query_cache" value="false"/>

<property name="hibernate.search.default.directory_provider" value="ram"/>

<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<!-- Note: *Everything* else provided by artificer.properties -->
</properties>
</persistence-unit>
</persistence>
6 changes: 6 additions & 0 deletions test/pom.xml
Expand Up @@ -73,6 +73,12 @@
<artifactId>artificer-integration-switchyard</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.overlord</groupId>
<artifactId>overlord-commons-ant</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>javax.jms</groupId>
Expand Down
4 changes: 1 addition & 3 deletions test/src/test/resources/artificer.properties
Expand Up @@ -24,6 +24,4 @@ hibernate.cache.use_second_level_cache = false

# Hibernate Search (note that *any* Hibernate Search settings can be used)
hibernate.search.default.directory_provider = ram
hibernate.search.default.indexBase = lucene/indexes

hibernate.hbm2ddl.auto = create-drop
hibernate.search.default.indexBase = lucene/indexes

0 comments on commit 6dd6d07

Please sign in to comment.