Skip to content

Commit 14bcaaf

Browse files
committed
Add support for Test Containers
1 parent 7a78103 commit 14bcaaf

File tree

52 files changed

+1280
-523
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1280
-523
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,16 @@ Or, if you prefer reading books, you are going to love my [High-Performance Java
378378

379379
#### Contributing Guide
380380

381-
The project uses [Maven Toolchains](https://maven.apache.org/guides/mini/guide-using-toolchains.html) as different modules are compiled and tested using different Java versions. Hibernate Types 6 requires Java 17 while Hibernate Types 4 compiles with Java 1.6.
381+
The project uses [Maven Toolchains](https://maven.apache.org/guides/mini/guide-using-toolchains.html) as different modules are compiled and tested using different Java versions. Hibernate Types 6 requires Java 17 while the other modules are compiled with either Java 1.8 or 1.6.
382382

383383
To see how to configure Maven Toolchains, check out [this article](https://vladmihalcea.com/maven-and-java-multi-version-modules/).
384384

385385
The project uses various database systems for integration testing, and you can configure the JDBC connection settings using the
386-
`DatasourceProvider` instances (e.g., `PostgreSQLDataSourceProvider`).
386+
`DatasourceProvider` instances (e.g., `PostgreSQLDataSourceProvider`), and the project uses Testcontainers to bootstrap a Docker container
387+
with the required Oracle, SQL Server, PostgreSQL, or MySQL instance on demand.
388+
389+
> If you are a regular contributor, it's advisable to set up the required database locally or use the Docker Compose configuration provided in the `docker` folder,
390+
> as bootstrapping the containers on demand is slower, and your tests are going to take longer to run.
387391
388392
If you want to fix an issue or add support for a new feature, please provide the associated integration test case that proves the improvement is working as expected.
389393

hibernate-types-4/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<dependency>
3535
<groupId>com.fasterxml.jackson.core</groupId>
3636
<artifactId>jackson-databind</artifactId>
37-
<version>${jackson.version}</version>
37+
<version>${jackson-databind.version}</version>
3838
<scope>provided</scope>
3939
<optional>true</optional>
4040
</dependency>
@@ -77,7 +77,7 @@
7777
<hibernate.version>4.2.21.Final</hibernate.version>
7878
<postgresql.version>9.4-1202-jdbc4</postgresql.version>
7979

80-
<jackson.version>2.7.9.6</jackson.version>
80+
<jackson-databind.version>2.12.6.1</jackson-databind.version>
8181
<guava.version>12.0</guava.version>
8282
<moneta.version>1.4.2</moneta.version>
8383
</properties>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.vladmihalcea.hibernate.util.providers;
2+
3+
import org.testcontainers.containers.JdbcDatabaseContainer;
4+
5+
import javax.sql.DataSource;
6+
import java.sql.Connection;
7+
import java.sql.SQLException;
8+
9+
/**
10+
* @author Vlad Mihalcea
11+
*/
12+
public abstract class AbstractContainerDataSourceProvider implements DataSourceProvider {
13+
14+
@Override
15+
public DataSource dataSource() {
16+
DataSource dataSource = newDataSource();
17+
Connection connection = null;
18+
try {
19+
connection = dataSource.getConnection();
20+
return dataSource;
21+
} catch (SQLException e) {
22+
Database database = database();
23+
if(database.getContainer() == null) {
24+
database.initContainer(username(), password());
25+
}
26+
return newDataSource();
27+
} finally {
28+
if (connection != null) {
29+
try {
30+
connection.close();
31+
} catch (SQLException ignore) {
32+
}
33+
}
34+
}
35+
}
36+
37+
@Override
38+
public String url() {
39+
JdbcDatabaseContainer container = database().getContainer();
40+
return container != null ?
41+
container.getJdbcUrl() :
42+
defaultJdbcUrl();
43+
}
44+
45+
protected abstract String defaultJdbcUrl();
46+
47+
protected abstract DataSource newDataSource();
48+
}
Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,89 @@
11
package com.vladmihalcea.hibernate.util.providers;
22

3+
import org.testcontainers.containers.*;
4+
5+
import java.util.Collections;
6+
37
/**
48
* @author Vlad Mihalcea
59
*/
610
public enum Database {
711
HSQLDB,
812
H2,
9-
POSTGRESQL,
10-
ORACLE,
11-
MYSQL,
12-
SQLSERVER,
13-
COCKROACHDB
13+
POSTGRESQL {
14+
@Override
15+
protected JdbcDatabaseContainer newJdbcDatabaseContainer() {
16+
return new PostgreSQLContainer("postgres:13.7");
17+
}
18+
},
19+
ORACLE {
20+
@Override
21+
protected JdbcDatabaseContainer newJdbcDatabaseContainer() {
22+
return new OracleContainer("gvenzl/oracle-xe:21.3.0-slim");
23+
}
24+
25+
@Override
26+
protected boolean supportsDatabaseName() {
27+
return false;
28+
}
29+
},
30+
MYSQL {
31+
@Override
32+
protected JdbcDatabaseContainer newJdbcDatabaseContainer() {
33+
return new MySQLContainer("mysql:8.0");
34+
}
35+
},
36+
SQLSERVER {
37+
@Override
38+
protected JdbcDatabaseContainer newJdbcDatabaseContainer() {
39+
return new MSSQLServerContainer("mcr.microsoft.com/mssql/server:2019-latest");
40+
}
41+
42+
@Override
43+
protected boolean supportsDatabaseName() {
44+
return false;
45+
}
46+
47+
@Override
48+
protected boolean supportsCredentials() {
49+
return false;
50+
}
51+
},
52+
COCKROACHDB;
53+
54+
private JdbcDatabaseContainer container;
55+
56+
public JdbcDatabaseContainer getContainer() {
57+
return container;
58+
}
59+
60+
public void initContainer(String username, String password) {
61+
container = (JdbcDatabaseContainer) newJdbcDatabaseContainer()
62+
.withEnv(Collections.singletonMap("ACCEPT_EULA", "Y"));
63+
if(supportsDatabaseName()) {
64+
container.withDatabaseName("high-performance-java-persistence");
65+
}
66+
if(supportsCredentials()) {
67+
container.withUsername(username).withPassword(password);
68+
}
69+
container.withTmpFs(Collections.singletonMap("/testtmpfs", "rw"));
70+
container.start();
71+
}
72+
73+
protected JdbcDatabaseContainer newJdbcDatabaseContainer() {
74+
throw new UnsupportedOperationException(
75+
String.format(
76+
"The [%s] database was not configured to use Testcontainers!",
77+
name()
78+
)
79+
);
80+
}
81+
82+
protected boolean supportsDatabaseName() {
83+
return true;
84+
}
85+
86+
protected boolean supportsCredentials() {
87+
return true;
88+
}
1489
}

hibernate-types-4/src/test/java/com/vladmihalcea/hibernate/util/providers/MySQLDataSourceProvider.java

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/**
99
* @author Vlad Mihalcea
1010
*/
11-
public class MySQLDataSourceProvider implements DataSourceProvider {
11+
public class MySQLDataSourceProvider extends AbstractContainerDataSourceProvider {
1212

1313
private boolean rewriteBatchedStatements = true;
1414

@@ -76,19 +76,19 @@ public String hibernateDialect() {
7676
}
7777

7878
@Override
79-
public DataSource dataSource() {
79+
protected String defaultJdbcUrl() {
80+
return "jdbc:mysql://localhost/high_performance_java_persistence?useSSL=false";
81+
}
82+
83+
protected DataSource newDataSource() {
8084
MysqlDataSource dataSource = new MysqlDataSource();
81-
dataSource.setURL("jdbc:mysql://localhost/high_performance_java_persistence?useSSL=false&" +
82-
"rewriteBatchedStatements=" + rewriteBatchedStatements +
83-
"&cachePrepStmts=" + cachePrepStmts +
84-
"&useServerPrepStmts=" + useServerPrepStmts +
85-
"&useTimezone=" + useTimezone +
86-
"&useJDBCCompliantTimezoneShift=" + useJDBCCompliantTimezoneShift +
87-
"&useLegacyDatetimeCode=" + useLegacyDatetimeCode
88-
89-
);
90-
dataSource.setUser("mysql");
91-
dataSource.setPassword("admin");
85+
dataSource.setURL(url());
86+
dataSource.setUser(username());
87+
dataSource.setPassword(password());
88+
dataSource.setRewriteBatchedStatements(rewriteBatchedStatements);
89+
dataSource.setCachePrepStmts(cachePrepStmts);
90+
dataSource.setUseServerPrepStmts(useServerPrepStmts);
91+
9292
return dataSource;
9393
}
9494

@@ -101,22 +101,19 @@ public Class<? extends DataSource> dataSourceClassName() {
101101
public Properties dataSourceProperties() {
102102
Properties properties = new Properties();
103103
properties.setProperty("url", url());
104+
properties.setProperty("user", username());
105+
properties.setProperty("password", password());
104106
return properties;
105107
}
106108

107-
@Override
108-
public String url() {
109-
return "jdbc:mysql://localhost/high_performance_java_persistence?user=mysql&password=admin";
110-
}
111-
112109
@Override
113110
public String username() {
114-
return null;
111+
return "mysql";
115112
}
116113

117114
@Override
118115
public String password() {
119-
return null;
116+
return "admin";
120117
}
121118

122119
@Override
@@ -127,12 +124,12 @@ public Database database() {
127124
@Override
128125
public String toString() {
129126
return "MySQLDataSourceProvider{" +
130-
"rewriteBatchedStatements=" + rewriteBatchedStatements +
131-
", cachePrepStmts=" + cachePrepStmts +
132-
", useServerPrepStmts=" + useServerPrepStmts +
133-
", useTimezone=" + useTimezone +
134-
", useJDBCCompliantTimezoneShift=" + useJDBCCompliantTimezoneShift +
135-
", useLegacyDatetimeCode=" + useLegacyDatetimeCode +
136-
'}';
127+
"rewriteBatchedStatements=" + rewriteBatchedStatements +
128+
", cachePrepStmts=" + cachePrepStmts +
129+
", useServerPrepStmts=" + useServerPrepStmts +
130+
", useTimezone=" + useTimezone +
131+
", useJDBCCompliantTimezoneShift=" + useJDBCCompliantTimezoneShift +
132+
", useLegacyDatetimeCode=" + useLegacyDatetimeCode +
133+
'}';
137134
}
138135
}

hibernate-types-4/src/test/java/com/vladmihalcea/hibernate/util/providers/OracleDataSourceProvider.java

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
11
package com.vladmihalcea.hibernate.util.providers;
22

3-
import com.vladmihalcea.hibernate.util.ReflectionUtils;
3+
import oracle.jdbc.pool.OracleDataSource;
44
import org.hibernate.dialect.Oracle10gDialect;
5+
import org.testcontainers.containers.JdbcDatabaseContainer;
56

67
import javax.sql.DataSource;
78
import java.util.Properties;
89

910
/**
1011
* @author Vlad Mihalcea
1112
*/
12-
public class OracleDataSourceProvider implements DataSourceProvider {
13+
public class OracleDataSourceProvider extends AbstractContainerDataSourceProvider {
14+
1315
@Override
1416
public String hibernateDialect() {
1517
return Oracle10gDialect.class.getName();
1618
}
1719

1820
@Override
19-
public DataSource dataSource() {
21+
public String defaultJdbcUrl() {
22+
return "jdbc:oracle:thin:@localhost:1521/xe";
23+
}
24+
25+
@Override
26+
public DataSource newDataSource() {
2027
try {
21-
DataSource dataSource = ReflectionUtils.newInstance("oracle.jdbc.pool.OracleDataSource");
22-
ReflectionUtils.invokeSetter(dataSource, "databaseName", "high_performance_java_persistence");
23-
ReflectionUtils.invokeSetter(dataSource, "URL", url());
24-
ReflectionUtils.invokeSetter(dataSource, "user", "oracle");
25-
ReflectionUtils.invokeSetter(dataSource, "password", "admin");
28+
OracleDataSource dataSource = new OracleDataSource();
29+
JdbcDatabaseContainer container = database().getContainer();
30+
if(container == null) {
31+
dataSource.setDatabaseName("high_performance_java_persistence");
32+
} else {
33+
dataSource.setDatabaseName(container.getDatabaseName());
34+
}
35+
dataSource.setURL(url());
36+
dataSource.setUser(username());
37+
dataSource.setPassword(password());
2638
return dataSource;
2739
} catch (Exception e) {
2840
throw new IllegalStateException(e);
@@ -31,7 +43,7 @@ public DataSource dataSource() {
3143

3244
@Override
3345
public Class<? extends DataSource> dataSourceClassName() {
34-
return ReflectionUtils.getClass("oracle.jdbc.pool.OracleDataSource");
46+
return OracleDataSource.class;
3547
}
3648

3749
@Override
@@ -44,11 +56,6 @@ public Properties dataSourceProperties() {
4456
return properties;
4557
}
4658

47-
@Override
48-
public String url() {
49-
return "jdbc:oracle:thin:@localhost:1521/xe";
50-
}
51-
5259
@Override
5360
public String username() {
5461
return "oracle";

hibernate-types-4/src/test/java/com/vladmihalcea/hibernate/util/providers/PostgreSQLDataSourceProvider.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,24 @@
99
/**
1010
* @author Vlad Mihalcea
1111
*/
12-
public class PostgreSQLDataSourceProvider implements DataSourceProvider {
12+
public class PostgreSQLDataSourceProvider extends AbstractContainerDataSourceProvider {
1313

1414
@Override
1515
public String hibernateDialect() {
1616
return PostgreSQL82Dialect.class.getName();
1717
}
1818

1919
@Override
20-
public DataSource dataSource() {
20+
protected String defaultJdbcUrl() {
21+
return "jdbc:postgresql://localhost/high_performance_java_persistence";
22+
}
23+
24+
protected DataSource newDataSource() {
2125
PGSimpleDataSource dataSource = new PGSimpleDataSource();
22-
dataSource.setDatabaseName("high_performance_java_persistence");
23-
dataSource.setServerName("localhost");
24-
dataSource.setUser("postgres");
25-
dataSource.setPassword("admin");
26+
dataSource.setUrl(url());
27+
dataSource.setUser(username());
28+
dataSource.setPassword(password());
29+
2630
return dataSource;
2731
}
2832

@@ -41,11 +45,6 @@ public Properties dataSourceProperties() {
4145
return properties;
4246
}
4347

44-
@Override
45-
public String url() {
46-
return null;
47-
}
48-
4948
@Override
5049
public String username() {
5150
return "postgres";

0 commit comments

Comments
 (0)