Skip to content

Commit a08b095

Browse files
aahlenstphilwebb
authored andcommitted
Add support for jOOQ
Add auto-configuration and a starter POM for jOOQ. See gh-2804
1 parent 9da14fe commit a08b095

File tree

13 files changed

+738
-0
lines changed

13 files changed

+738
-0
lines changed

spring-boot-autoconfigure/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@
510510
<artifactId>aspectjweaver</artifactId>
511511
<optional>true</optional>
512512
</dependency>
513+
<dependency>
514+
<groupId>org.jooq</groupId>
515+
<artifactId>jooq</artifactId>
516+
<optional>true</optional>
517+
</dependency>
513518
<!-- Annotation processing -->
514519
<dependency>
515520
<groupId>org.springframework.boot</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.jooq.ConnectionProvider;
22+
import org.jooq.DSLContext;
23+
import org.jooq.ExecuteListenerProvider;
24+
import org.jooq.RecordListenerProvider;
25+
import org.jooq.RecordMapperProvider;
26+
import org.jooq.SQLDialect;
27+
import org.jooq.TransactionProvider;
28+
import org.jooq.VisitListenerProvider;
29+
import org.jooq.conf.Settings;
30+
import org.jooq.impl.DataSourceConnectionProvider;
31+
import org.jooq.impl.DefaultConfiguration;
32+
import org.jooq.impl.DefaultDSLContext;
33+
import org.jooq.impl.DefaultExecuteListenerProvider;
34+
import org.springframework.beans.factory.annotation.Autowired;
35+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
36+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
37+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
38+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
39+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
40+
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
41+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
42+
import org.springframework.context.annotation.Bean;
43+
import org.springframework.context.annotation.Configuration;
44+
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
45+
import org.springframework.transaction.PlatformTransactionManager;
46+
import org.springframework.util.StringUtils;
47+
48+
/**
49+
* {@link EnableAutoConfiguration Auto-configuration} for JOOQ.
50+
*
51+
* @author Andreas Ahlenstorf
52+
* @since 1.3.0
53+
*/
54+
@Configuration
55+
@ConditionalOnClass(DSLContext.class)
56+
@ConditionalOnBean(DataSource.class)
57+
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
58+
public class JooqAutoConfiguration {
59+
60+
@Bean
61+
@ConditionalOnMissingBean(DataSourceConnectionProvider.class)
62+
public DataSourceConnectionProvider dataSourceConnectionProvider(DataSource dataSource) {
63+
return new DataSourceConnectionProvider(new TransactionAwareDataSourceProxy(
64+
dataSource));
65+
}
66+
67+
@Bean
68+
@ConditionalOnBean(PlatformTransactionManager.class)
69+
public TransactionProvider transactionProvider(PlatformTransactionManager txManager) {
70+
return new SpringTransactionProvider(txManager);
71+
}
72+
73+
@Bean
74+
public ExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider() {
75+
return new DefaultExecuteListenerProvider(new JooqExceptionTranslator());
76+
}
77+
78+
@Configuration
79+
@ConditionalOnMissingBean(DSLContext.class)
80+
@EnableConfigurationProperties(JooqProperties.class)
81+
public static class DslContextConfiguration {
82+
83+
@Autowired
84+
private JooqProperties properties = new JooqProperties();
85+
86+
@Autowired
87+
private ConnectionProvider connectionProvider;
88+
89+
@Autowired(required = false)
90+
private TransactionProvider transactionProvider;
91+
92+
@Autowired(required = false)
93+
private RecordMapperProvider recordMapperProvider;
94+
95+
@Autowired(required = false)
96+
private Settings settings;
97+
98+
@Autowired(required = false)
99+
private RecordListenerProvider[] recordListenerProviders;
100+
101+
@Autowired
102+
private ExecuteListenerProvider[] executeListenerProviders;
103+
104+
@Autowired(required = false)
105+
private VisitListenerProvider[] visitListenerProviders;
106+
107+
@Bean
108+
public DefaultDSLContext dslContext(org.jooq.Configuration configuration) {
109+
return new DefaultDSLContext(configuration);
110+
}
111+
112+
@Bean
113+
@ConditionalOnMissingBean(org.jooq.Configuration.class)
114+
public DefaultConfiguration jooqConfiguration() {
115+
DefaultConfiguration configuration = new DefaultConfiguration();
116+
if (!StringUtils.isEmpty(this.properties.getSqlDialect())) {
117+
configuration.set(SQLDialect.valueOf(this.properties.getSqlDialect()));
118+
}
119+
configuration.set(this.connectionProvider);
120+
if (this.transactionProvider != null) {
121+
configuration.set(this.transactionProvider);
122+
}
123+
if (this.recordMapperProvider != null) {
124+
configuration.set(this.recordMapperProvider);
125+
}
126+
if (this.settings != null) {
127+
configuration.set(this.settings);
128+
}
129+
configuration.set(this.recordListenerProviders);
130+
configuration.set(this.executeListenerProviders);
131+
configuration.set(this.visitListenerProviders);
132+
return configuration;
133+
}
134+
135+
}
136+
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import java.sql.SQLException;
20+
21+
import org.apache.commons.logging.Log;
22+
import org.apache.commons.logging.LogFactory;
23+
import org.jooq.ExecuteContext;
24+
import org.jooq.SQLDialect;
25+
import org.jooq.impl.DefaultExecuteListener;
26+
import org.springframework.dao.DataAccessException;
27+
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
28+
import org.springframework.jdbc.support.SQLExceptionTranslator;
29+
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
30+
31+
/**
32+
* Transforms {@link java.sql.SQLException} into a Spring-specific @{link
33+
* DataAccessException}.
34+
*
35+
* @author Lukas Eder
36+
* @author Andreas Ahlenstorf
37+
* @author Phillip Webb
38+
*/
39+
class JooqExceptionTranslator extends DefaultExecuteListener {
40+
41+
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
42+
43+
private static final Log logger = LogFactory.getLog(JooqExceptionTranslator.class);
44+
45+
@Override
46+
public void exception(ExecuteContext context) {
47+
SQLExceptionTranslator translator = getTranslator(context);
48+
// The exception() callback is not only triggered for SQL exceptions but also for
49+
// "normal" exceptions. In those cases sqlException() returns null.
50+
SQLException exception = context.sqlException();
51+
while (exception != null) {
52+
handle(context, translator, exception);
53+
exception = exception.getNextException();
54+
}
55+
}
56+
57+
private SQLExceptionTranslator getTranslator(ExecuteContext context) {
58+
SQLDialect dialect = context.configuration().dialect();
59+
if (dialect != null) {
60+
return new SQLErrorCodeSQLExceptionTranslator(dialect.name());
61+
}
62+
return new SQLStateSQLExceptionTranslator();
63+
}
64+
65+
/**
66+
* Handle a single exception in the chain. SQLExceptions might be nested multiple
67+
* levels deep. The outermost exception is usually the least interesting one
68+
* ("Call getNextException to see the cause."). Therefore the innermost exception is
69+
* propagated and all other exceptions are logged.
70+
* @param context the execute context
71+
* @param translator the exception translator
72+
* @param exception the exception
73+
*/
74+
private void handle(ExecuteContext context, SQLExceptionTranslator translator,
75+
SQLException exception) {
76+
DataAccessException translated = translate(context, translator, exception);
77+
if (exception.getNextException() == null) {
78+
context.exception(translated);
79+
}
80+
else {
81+
logger.error("Execution of SQL statement failed.", translated);
82+
}
83+
}
84+
85+
private DataAccessException translate(ExecuteContext context,
86+
SQLExceptionTranslator translator, SQLException exception) {
87+
return translator.translate("jOOQ", context.sql(), exception);
88+
}
89+
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
21+
/**
22+
* Configuration properties for the JOOQ database library.
23+
*
24+
* @author Andreas Ahlenstorf
25+
* @since 1.3.0
26+
*/
27+
@ConfigurationProperties(prefix = "spring.jooq")
28+
public class JooqProperties {
29+
30+
/**
31+
* SQLDialect JOOQ used when communicating with the configured datasource, e.g.
32+
* "POSTGRES".
33+
*/
34+
private String sqlDialect;
35+
36+
public String getSqlDialect() {
37+
return this.sqlDialect;
38+
}
39+
40+
public void setSqlDialect(String sqlDialect) {
41+
this.sqlDialect = sqlDialect;
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import org.jooq.Transaction;
20+
import org.springframework.transaction.TransactionStatus;
21+
22+
/**
23+
* Adapts a Spring transaction for JOOQ.
24+
*
25+
* @author Lukas Eder
26+
* @author Andreas Ahlenstorf
27+
* @author Phillip Webb
28+
*/
29+
class SpringTransaction implements Transaction {
30+
31+
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
32+
33+
private final TransactionStatus transactionStatus;
34+
35+
public SpringTransaction(TransactionStatus transactionStatus) {
36+
this.transactionStatus = transactionStatus;
37+
}
38+
39+
public TransactionStatus getTxStatus() {
40+
return this.transactionStatus;
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import org.jooq.TransactionContext;
20+
import org.jooq.TransactionProvider;
21+
import org.springframework.transaction.PlatformTransactionManager;
22+
import org.springframework.transaction.TransactionDefinition;
23+
import org.springframework.transaction.TransactionStatus;
24+
import org.springframework.transaction.support.DefaultTransactionDefinition;
25+
26+
/**
27+
* Allows Spring Transaction to be used with JOOQ.
28+
*
29+
* @author Lukas Eder
30+
* @author Andreas Ahlenstorf
31+
* @author Phillip Webb
32+
*/
33+
class SpringTransactionProvider implements TransactionProvider {
34+
35+
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
36+
37+
private final PlatformTransactionManager transactionManager;
38+
39+
SpringTransactionProvider(PlatformTransactionManager transactionManager) {
40+
this.transactionManager = transactionManager;
41+
}
42+
43+
@Override
44+
public void begin(TransactionContext context) {
45+
TransactionDefinition definition = new DefaultTransactionDefinition(
46+
TransactionDefinition.PROPAGATION_NESTED);
47+
TransactionStatus status = this.transactionManager.getTransaction(definition);
48+
context.transaction(new SpringTransaction(status));
49+
}
50+
51+
@Override
52+
public void commit(TransactionContext ctx) {
53+
this.transactionManager.commit(getTransactionStatus(ctx));
54+
}
55+
56+
@Override
57+
public void rollback(TransactionContext ctx) {
58+
this.transactionManager.rollback(getTransactionStatus(ctx));
59+
}
60+
61+
private TransactionStatus getTransactionStatus(TransactionContext ctx) {
62+
SpringTransaction transaction = (SpringTransaction) ctx.transaction();
63+
return transaction.getTxStatus();
64+
}
65+
66+
}

0 commit comments

Comments
 (0)