Skip to content

Commit ad2ac3b

Browse files
author
bwajtr
committed
Jooq and JDBCTemplate implementation of scenario eleven - call static statement (not prepared statement).
1 parent 7c0107c commit ad2ac3b

8 files changed

Lines changed: 105 additions & 6 deletions

File tree

dbscenarios.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
8. Update entities one-to-many relation - add two items, update two items and delete one item
99
9. Complex select - construct select where conditions based on some boolean conditions + throw in some joins
1010
10. Call stored procedure/function and process results
11-
11. Optimistic locking
11+
11. Execute query using JDBC simple Statement (not PreparedStatement)

src/main/java/com/clevergang/dbtests/DbTestsApplication.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
import org.jooq.conf.RenderNameStyle;
44
import org.jooq.conf.Settings;
5+
import org.jooq.conf.StatementType;
6+
import org.springframework.beans.factory.annotation.Qualifier;
57
import org.springframework.boot.SpringApplication;
68
import org.springframework.boot.autoconfigure.SpringBootApplication;
79
import org.springframework.context.annotation.Bean;
810
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.context.annotation.Primary;
912

1013
@SpringBootApplication
1114
@Configuration
1215
public class DbTestsApplication {
1316

1417
@Bean
18+
@Primary
1519
public Settings jooqSettings() {
1620
Settings ret = new Settings();
1721
ret.withRenderSchema(false);
@@ -20,7 +24,15 @@ public Settings jooqSettings() {
2024
return ret;
2125
}
2226

23-
public static void main(String[] args) {
24-
SpringApplication.run(DbTestsApplication.class, args);
25-
}
27+
@Bean
28+
@Qualifier("static-statement-jooq-settings")
29+
public Settings jooqStaticStatementSettings() {
30+
Settings ret = jooqSettings();
31+
ret.withStatementType(StatementType.STATIC_STATEMENT);
32+
return ret;
33+
}
34+
35+
public static void main(String[] args) {
36+
SpringApplication.run(DbTestsApplication.class, args);
37+
}
2638
}

src/main/java/com/clevergang/dbtests/Scenarios.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,31 @@ public void scenarioNine() {
218218

219219
/**
220220
* Execute stored procedure/function and process results
221-
*
222221
*/
223222
public void scenarioTen() {
224223
RegisterEmployeeOutput output = repository.callRegisterEmployee("Bretislav", "Wajtr", "bretislav.wajtr@test.com", new BigDecimal(40000), "MyDepartment", "MyCompany");
225224

226225
logger.info("scenarioTen output: {}", output);
227226
}
228227

228+
229+
/**
230+
* Execute query using JDBC simple Statement (not PreparedStatement)
231+
*
232+
* Motivation why we need the "static statement" feature: In 96% of the cases, you’re better off writing
233+
* a PreparedStatement rather than a static statement - it's safer (sql injection),
234+
* easier (complex data types like dates) and sometimes faster (prepared statements reuse). However, there are
235+
* edge cases for complex queries and lot of data where it's actually faster to use simple statement query, because
236+
* your database’s cost-based optimiser or planner obtains some heads-up about what kind of data is really going to
237+
* be affected by the query and can therefore execute the query faster.
238+
*
239+
* Good SQL API framework should offer way how to execute simple static statements.
240+
*/
241+
public void scenarioEleven() {
242+
Company output = repository.findCompanyUsingSimpleStaticStatement(1);
243+
244+
logger.info("Output: {}", output);
245+
}
229246
}
230247

231248

src/main/java/com/clevergang/dbtests/repository/api/DataRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public interface DataRepository {
1818

1919
Company findCompany(Integer pid);
2020

21+
Company findCompanyUsingSimpleStaticStatement(Integer pid);
22+
2123
// departments
2224

2325
Department findDepartment(Integer pid);

src/main/java/com/clevergang/dbtests/repository/impl/jdbctemplate/JDBCDataRepositoryImpl.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,33 @@ public Company findCompany(Integer pid) {
5858
return company;
5959
}
6060

61+
@Override
62+
public Company findCompanyUsingSimpleStaticStatement(Integer pid) {
63+
String query;
64+
query = "SELECT pid, address, name " +
65+
"FROM company " +
66+
"WHERE pid = " + pid;
67+
68+
RowMapper<Company> mapper = (rs, rowNum) -> {
69+
Company row = new Company();
70+
row.setPid(rs.getInt("pid"));
71+
row.setAddress(rs.getString("address"));
72+
row.setName(rs.getString("name"));
73+
return row;
74+
};
75+
76+
// In Spring's JDBCTemplate it's pretty easy to execute the query using static Statement (not the PreparedStatement):
77+
// In this DAO we autowired NamedParameterJdbcTemplate, which does not allow static statements, but this class
78+
// is just a wrapper around original JdbcTemplate which allows static statements -> so we used getJdbcOperations()
79+
// to get such original object and then just used one of it's methods which internally calls the static
80+
// statement (see javadoc for each JdbcOperations#query*() method)
81+
Company company = jdbcTemplate.getJdbcOperations().queryForObject(query, mapper);
82+
83+
logger.info("Found company: " + company);
84+
85+
return company;
86+
}
87+
6188
@Override
6289
public Department findDepartment(Integer pid) {
6390
String query;

src/main/java/com/clevergang/dbtests/repository/impl/jooq/JooqDataRepositoryImpl.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
import com.clevergang.dbtests.repository.impl.jooq.generated.tables.records.DepartmentRecord;
88
import com.clevergang.dbtests.repository.impl.jooq.generated.tables.records.EmployeeRecord;
99
import org.jooq.*;
10+
import org.jooq.conf.Settings;
11+
import org.jooq.impl.DSL;
1012
import org.slf4j.Logger;
1113
import org.slf4j.LoggerFactory;
1214
import org.springframework.beans.factory.annotation.Autowired;
15+
import org.springframework.beans.factory.annotation.Qualifier;
1316
import org.springframework.stereotype.Repository;
1417

1518
import java.io.IOException;
@@ -21,6 +24,7 @@
2124
import java.sql.Date;
2225
import java.util.Collection;
2326
import java.util.List;
27+
import java.util.concurrent.atomic.AtomicReference;
2428
import java.util.stream.Collectors;
2529

2630
import static com.clevergang.dbtests.repository.impl.jooq.generated.Tables.*;
@@ -39,6 +43,10 @@ public class JooqDataRepositoryImpl implements DataRepository {
3943
@Autowired
4044
private DSLContext create;
4145

46+
@Autowired
47+
@Qualifier("static-statement-jooq-settings")
48+
private Settings staticStatementSettings;
49+
4250
@Override
4351
public Company findCompany(Integer pid) {
4452
logger.info("Finding Company by ID using JOOQ");
@@ -49,10 +57,34 @@ public Company findCompany(Integer pid) {
4957
.fetchOneInto(Company.class);
5058

5159
logger.info("Found company: " + company);
52-
5360
return company;
5461
}
5562

63+
@Override
64+
public Company findCompanyUsingSimpleStaticStatement(Integer pid) {
65+
// This is the only way I found how to actually do a static Statement in JOOQ (so not the default
66+
// prepared statement, but simple static statement where you do not bind values). We create new instance of DSLContext
67+
// using Settings which are configured to use static statements. The important factor here is
68+
// to create new context based on the Connection of autowired DSLContext (therefore the usage of create.connection()
69+
// method) - this is the only way to ensure that the this static statement will be executed in same transaction
70+
// as other statements called through autowired DSLContext.
71+
//
72+
// If you create new DSLContext using the some generally available (autowired) datasource, then you'll create
73+
// completely new JOOQ configuration with completely new connection -> such statements will be executed in
74+
// their own transactions!
75+
//
76+
// FIXME: I don't like the usage of AtomicReference here to get the value out of the lambda. If anyone has better class where to store the value, please advise.
77+
AtomicReference<Company> reference = new AtomicReference<>();
78+
create.connection(connection -> {
79+
DSLContext staticStatement = DSL.using(connection, create.dialect(), staticStatementSettings);
80+
reference.set(staticStatement.
81+
selectFrom(COMPANY)
82+
.where(COMPANY.PID.eq(pid))
83+
.fetchOneInto(Company.class));
84+
});
85+
return reference.get();
86+
}
87+
5688
@Override
5789
public Department findDepartment(Integer pid) {
5890
return create.

src/test/java/com/clevergang/dbtests/JdbcTemplateScenariosTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,9 @@ public void scenarioTen() {
7878
scenarios.scenarioTen();
7979
}
8080

81+
@Test
82+
public void scenarioEleven() {
83+
scenarios.scenarioEleven();
84+
}
8185

8286
}

src/test/java/com/clevergang/dbtests/JooqScenariosTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ public void scenarioTen() {
7878
scenarios.scenarioTen();
7979
}
8080

81+
@Test
82+
public void scenarioEleven() {
83+
scenarios.scenarioEleven();
84+
}
85+
8186
}
8287

8388

0 commit comments

Comments
 (0)