Skip to content

Commit f6ed1f5

Browse files
gavinkingDavideD
authored andcommitted
[#1602] Wire in SqlExceptionHelper which is now useful in Vert.x 4.4.0
1 parent 84d32fb commit f6ed1f5

File tree

8 files changed

+190
-11
lines changed

8 files changed

+190
-11
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/ReactiveConnectionPool.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.hibernate.reactive.pool;
77

88
import org.hibernate.Incubating;
9+
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
910
import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder;
1011
import org.hibernate.service.Service;
1112

@@ -41,12 +42,27 @@ public interface ReactiveConnectionPool extends Service {
4142
*/
4243
CompletionStage<ReactiveConnection> getConnection();
4344

45+
/**
46+
* Obtain a reactive connection, returning the connection
47+
* via a {@link CompletionStage} and overriding the default
48+
* {@link SqlExceptionHelper} for the pool.
49+
*/
50+
CompletionStage<ReactiveConnection> getConnection(SqlExceptionHelper sqlExceptionHelper);
51+
4452
/**
4553
* Obtain a reactive connection for the given tenant id,
4654
* returning the connection via a {@link CompletionStage}.
4755
*/
4856
CompletionStage<ReactiveConnection> getConnection(String tenantId);
4957

58+
/**
59+
* Obtain a reactive connection for the given tenant id,
60+
* returning the connection via a {@link CompletionStage}
61+
* and overriding the default {@link SqlExceptionHelper}
62+
* for the pool.
63+
*/
64+
CompletionStage<ReactiveConnection> getConnection(String tenantId, SqlExceptionHelper sqlExceptionHelper);
65+
5066
/**
5167
* Obtain a lazily-initializing reactive connection. The
5268
* actual connection might be made when the returned

hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPool.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import java.util.ServiceLoader;
1515
import java.util.concurrent.CompletionStage;
1616

17+
import org.hibernate.engine.jdbc.spi.JdbcServices;
18+
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
1719
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
1820
import org.hibernate.internal.util.config.ConfigurationException;
1921
import org.hibernate.internal.util.config.ConfigurationHelper;
@@ -109,6 +111,7 @@ public static VertxDriver findByClassName(String className) {
109111

110112
private Pool pools;
111113
private SqlStatementLogger sqlStatementLogger;
114+
private SqlExceptionHelper sqlExceptionHelper;
112115
private URI uri;
113116
private ServiceRegistryImplementor serviceRegistry;
114117

@@ -151,6 +154,15 @@ protected SqlStatementLogger getSqlStatementLogger() {
151154
return sqlStatementLogger;
152155
}
153156

157+
@Override
158+
public SqlExceptionHelper getSqlExceptionHelper() {
159+
if ( sqlExceptionHelper == null ) {
160+
sqlExceptionHelper = serviceRegistry
161+
.getService( JdbcServices.class ).getSqlExceptionHelper();
162+
}
163+
return sqlExceptionHelper;
164+
}
165+
154166
/**
155167
* Create a new {@link Pool} for the given JDBC URL or database URI,
156168
* using the {@link VertxInstance} service to obtain an instance of

hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ExternalSqlClientPool.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.concurrent.CompletionStage;
99

10+
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
1011
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
1112
import org.hibernate.reactive.mutiny.Mutiny;
1213
import org.hibernate.reactive.stage.Stage;
@@ -47,10 +48,12 @@ public final class ExternalSqlClientPool extends SqlClientPool {
4748

4849
private final Pool pool;
4950
private final SqlStatementLogger sqlStatementLogger;
51+
private SqlExceptionHelper sqlExceptionHelper;
5052

51-
public ExternalSqlClientPool(Pool pool, SqlStatementLogger sqlStatementLogger) {
53+
public ExternalSqlClientPool(Pool pool, SqlStatementLogger sqlStatementLogger, SqlExceptionHelper sqlExceptionHelper) {
5254
this.pool = pool;
5355
this.sqlStatementLogger = sqlStatementLogger;
56+
this.sqlExceptionHelper = sqlExceptionHelper;
5457
}
5558

5659
@Override
@@ -63,13 +66,17 @@ protected SqlStatementLogger getSqlStatementLogger() {
6366
return sqlStatementLogger;
6467
}
6568

69+
@Override
70+
public SqlExceptionHelper getSqlExceptionHelper() {
71+
return sqlExceptionHelper;
72+
}
73+
6674
/**
6775
* Since this Service implementation does not implement @{@link org.hibernate.service.spi.Stoppable}
6876
* and we're only adapting an externally provided pool, we will not actually close such provided pool
6977
* when Hibernate ORM is shutdown (it doesn't own the lifecycle of this external component).
70-
* Therefore there is no need to wait for its shutdown and this method returns an already
78+
* Therefore, there is no need to wait for its shutdown and this method returns an already
7179
* successfully completed CompletionStage.
72-
* @return
7380
*/
7481
@Override
7582
public CompletionStage<Void> getCloseFuture() {

hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.concurrent.CompletionStage;
1515

1616
import org.hibernate.engine.jdbc.internal.FormatStyle;
17+
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
1718
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
1819
import org.hibernate.reactive.adaptor.impl.JdbcNull;
1920
import org.hibernate.reactive.adaptor.impl.ResultSetAdaptor;
@@ -25,6 +26,7 @@
2526

2627
import io.vertx.core.json.JsonArray;
2728
import io.vertx.core.json.JsonObject;
29+
import io.vertx.sqlclient.DatabaseException;
2830
import io.vertx.sqlclient.Pool;
2931
import io.vertx.sqlclient.PrepareOptions;
3032
import io.vertx.sqlclient.PropertyKind;
@@ -53,15 +55,17 @@ public class SqlClientConnection implements ReactiveConnection {
5355
private final static PropertyKind<Row> ORACLE_GENERATED_KEYS = PropertyKind.create( "generated-keys", Row.class );
5456

5557
private final SqlStatementLogger sqlStatementLogger;
58+
private final SqlExceptionHelper sqlExceptionHelper;
5659

5760
private final Pool pool;
5861
private final SqlConnection connection;
5962
private Transaction transaction;
6063

61-
SqlClientConnection(SqlConnection connection, Pool pool, SqlStatementLogger sqlStatementLogger) {
64+
SqlClientConnection(SqlConnection connection, Pool pool, SqlStatementLogger sqlStatementLogger, SqlExceptionHelper sqlExceptionHelper) {
6265
this.pool = pool;
6366
this.sqlStatementLogger = sqlStatementLogger;
6467
this.connection = connection;
68+
this.sqlExceptionHelper = sqlExceptionHelper;
6569
LOG.tracef( "Connection created: %s", connection );
6670
}
6771

@@ -151,6 +155,11 @@ private <T> T convertException(T rows, String sql, Throwable sqlException) {
151155
if ( sqlException == null ) {
152156
return rows;
153157
}
158+
if ( sqlException instanceof DatabaseException ) {
159+
DatabaseException de = (DatabaseException) sqlException;
160+
sqlException = sqlExceptionHelper
161+
.convert( new SQLException( de.getMessage(), de.getSqlState(), de.getErrorCode() ), "error executing SQL statement", sql );
162+
}
154163
return rethrow( sqlException );
155164
}
156165

hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientPool.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.concurrent.CompletionStage;
99

10+
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
1011
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
1112
import org.hibernate.reactive.pool.ReactiveConnection;
1213
import org.hibernate.reactive.pool.ReactiveConnectionPool;
@@ -44,6 +45,12 @@ public abstract class SqlClientPool implements ReactiveConnectionPool {
4445
*/
4546
protected abstract SqlStatementLogger getSqlStatementLogger();
4647

48+
/**
49+
* @return a Hibernate {@link SqlExceptionHelper} for converting
50+
* exceptions
51+
*/
52+
protected abstract SqlExceptionHelper getSqlExceptionHelper();
53+
4754
/**
4855
* Get a {@link Pool} for the specified tenant.
4956
* <p>
@@ -57,26 +64,45 @@ public abstract class SqlClientPool implements ReactiveConnectionPool {
5764
* @see ReactiveConnectionPool#getConnection(String)
5865
*/
5966
protected Pool getTenantPool(String tenantId) {
60-
throw new UnsupportedOperationException("multitenancy not supported by built-in SqlClientPool");
67+
throw new UnsupportedOperationException( "multitenancy not supported by built-in SqlClientPool" );
6168
}
6269

6370
@Override
6471
public CompletionStage<ReactiveConnection> getConnection() {
6572
return getConnectionFromPool( getPool() );
6673
}
6774

75+
@Override
76+
public CompletionStage<ReactiveConnection> getConnection(SqlExceptionHelper sqlExceptionHelper) {
77+
return getConnectionFromPool( getPool(), sqlExceptionHelper );
78+
}
79+
6880
@Override
6981
public CompletionStage<ReactiveConnection> getConnection(String tenantId) {
7082
return getConnectionFromPool( getTenantPool( tenantId ) );
7183
}
7284

85+
@Override
86+
public CompletionStage<ReactiveConnection> getConnection(String tenantId, SqlExceptionHelper sqlExceptionHelper) {
87+
return getConnectionFromPool( getTenantPool( tenantId ), sqlExceptionHelper );
88+
}
89+
7390
private CompletionStage<ReactiveConnection> getConnectionFromPool(Pool pool) {
7491
return pool.getConnection()
7592
.toCompletionStage().thenApply( this::newConnection );
7693
}
7794

95+
private CompletionStage<ReactiveConnection> getConnectionFromPool(Pool pool, SqlExceptionHelper sqlExceptionHelper) {
96+
return pool.getConnection()
97+
.toCompletionStage().thenApply( sqlConnection -> newConnection( sqlConnection, sqlExceptionHelper ) );
98+
}
99+
78100
private SqlClientConnection newConnection(SqlConnection connection) {
79-
return new SqlClientConnection( connection, getPool(), getSqlStatementLogger() );
101+
return newConnection( connection, getSqlExceptionHelper() );
102+
}
103+
104+
private SqlClientConnection newConnection(SqlConnection connection, SqlExceptionHelper sqlExceptionHelper) {
105+
return new SqlClientConnection( connection, getPool(), getSqlStatementLogger(), sqlExceptionHelper );
80106
}
81107

82108
@Override

hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
1616
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
1717
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
18+
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
1819
import org.hibernate.reactive.engine.jdbc.env.internal.ReactiveJdbcEnvironment;
1920
import org.hibernate.reactive.logging.impl.Log;
2021
import org.hibernate.reactive.logging.impl.LogCategory;
@@ -27,6 +28,7 @@
2728

2829
import io.vertx.sqlclient.spi.DatabaseMetadata;
2930

31+
import static java.util.function.Function.identity;
3032
import static org.hibernate.reactive.logging.impl.LoggerFactory.make;
3133
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
3234

@@ -95,8 +97,12 @@ private static Dialect checkDialect(Dialect dialect) {
9597

9698
private DialectResolutionInfo dialectResolutionInfo() {
9799
ReactiveConnectionPool connectionPool = registry.getService( ReactiveConnectionPool.class );
98-
return connectionPool.getConnection()
99-
.thenCompose( DialectBuilder::buildResolutionInfo ).toCompletableFuture().join();
100+
return connectionPool
101+
// The default SqlExceptionHelper in ORM requires the dialect, but we haven't create a dialect yet
102+
// so we need to override it at this stage, or we will have an exception.
103+
.getConnection( new SqlExceptionHelper( true ) )
104+
.thenCompose( DialectBuilder::buildResolutionInfo )
105+
.toCompletableFuture().join();
100106
}
101107

102108
private static CompletionStage<ReactiveDialectResolutionInfo> buildResolutionInfo(ReactiveConnection connection) {
@@ -106,7 +112,7 @@ private static CompletionStage<ReactiveDialectResolutionInfo> buildResolutionInf
106112
.thenCompose( handled -> {
107113
if ( handled.hasFailed() ) {
108114
// Something has already gone wrong: try to close the connection
109-
// and returns the original failure
115+
// and return the original failure
110116
return connection.close()
111117
.handle( (unused, throwable) -> handled.getResultAsCompletionStage() )
112118
.thenCompose( identity() );

hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyExceptionsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.Collection;
99
import java.util.List;
1010

11-
import org.hibernate.HibernateException;
11+
import org.hibernate.exception.ConstraintViolationException;
1212
import org.hibernate.reactive.mutiny.Mutiny;
1313

1414
import org.junit.Test;
@@ -27,7 +27,7 @@ protected Collection<Class<?>> annotatedEntities() {
2727
}
2828

2929
Class<?> getExpectedException() {
30-
return HibernateException.class;
30+
return ConstraintViolationException.class;
3131
}
3232

3333
@Test
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive;
7+
8+
import java.util.Objects;
9+
import java.util.Set;
10+
import java.util.concurrent.CompletionStage;
11+
12+
import org.hibernate.exception.ConstraintViolationException;
13+
14+
import org.junit.Test;
15+
16+
import io.vertx.ext.unit.TestContext;
17+
import jakarta.persistence.Entity;
18+
import jakarta.persistence.Id;
19+
import jakarta.persistence.Table;
20+
import jakarta.persistence.Version;
21+
22+
import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown;
23+
24+
public class ReactiveConstraintViolationTest extends BaseReactiveTest {
25+
26+
@Override
27+
protected Set<Class<?>> annotatedEntities() {
28+
return Set.of( GuineaPig.class );
29+
}
30+
31+
private CompletionStage<Void> populateDB() {
32+
return getSessionFactory()
33+
.withTransaction( s -> s.persist( new GuineaPig( 5, "Aloi" ) ) );
34+
}
35+
36+
@Test
37+
public void reactiveConstraintViolation(TestContext context) {
38+
test( context, assertThrown(
39+
ConstraintViolationException.class,
40+
populateDB()
41+
.thenCompose( v -> openSession() )
42+
.thenCompose( s -> s.persist( new GuineaPig( 5, "Aloi" ) )
43+
.thenCompose( i -> s.flush() ) )
44+
)
45+
);
46+
}
47+
48+
@Entity(name = "GuineaPig")
49+
@Table(name = "bad_pig")
50+
public static class GuineaPig {
51+
@Id
52+
private Integer id;
53+
private String name;
54+
@Version
55+
private int version;
56+
57+
public GuineaPig() {
58+
}
59+
60+
public GuineaPig(Integer id, String name) {
61+
this.id = id;
62+
this.name = name;
63+
}
64+
65+
public Integer getId() {
66+
return id;
67+
}
68+
69+
public void setId(Integer id) {
70+
this.id = id;
71+
}
72+
73+
public String getName() {
74+
return name;
75+
}
76+
77+
public void setName(String name) {
78+
this.name = name;
79+
}
80+
81+
@Override
82+
public String toString() {
83+
return id + ": " + name;
84+
}
85+
86+
@Override
87+
public boolean equals(Object o) {
88+
if ( this == o ) {
89+
return true;
90+
}
91+
if ( o == null || getClass() != o.getClass() ) {
92+
return false;
93+
}
94+
GuineaPig guineaPig = (GuineaPig) o;
95+
return Objects.equals( name, guineaPig.name );
96+
}
97+
98+
@Override
99+
public int hashCode() {
100+
return Objects.hash( name );
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)