Skip to content

Commit 044db0a

Browse files
author
Břetislav Wajtr
authored
Merge pull request #4 from bwajtr/fb-mybatis
Implementation for MyBatis
2 parents 8806a15 + c9948c6 commit 044db0a

17 files changed

Lines changed: 669 additions & 55 deletions

README.md

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
# Java repository layer frameworks comparison
22

3-
Comparison of usage of non-JPA SQL mapping (persistence) frameworks for Java (Jooq, Spring JDBCTemplate etc.).
3+
Comparison of usage of non-JPA SQL mapping (persistence) frameworks for Java (Jooq, Spring JDBCTemplate, MyBatis etc.).
44

5-
We are not comparing performance (that'll be maybe added later), but rather how are these frameworks used for everyday tasks.
5+
We are not comparing performance, but rather how are these frameworks used for everyday tasks.
66

7-
We prepared some common scenarios, which you typically need to implement data-centric application, and then we implemented these scenarios using various non-JPA DB layer frameworks.
7+
We prepared some common scenarios, which you typically need to implement data-centric application, and then we implemented these scenarios using various non-JPA DB layer frameworks. This project should serve
8+
- as point of reference when deciding for SQL mapping framework
9+
- as a template of common framework usage scenarios (see scenarios below)
10+
- to document best practices of such common usages (**comments are welcomed!**)
11+
12+
**Use code in the repository as you like (MIT License)**
813

914
## Frameworks compared
1015

1116
* Spring JDBCTemplate (see [implementation](src/main/java/com/clevergang/dbtests/repository/impl/jdbctemplate/JDBCDataRepositoryImpl.java))
1217
* jOOQ (see [implementation](src/main/java/com/clevergang/dbtests/repository/impl/jooq/JooqDataRepositoryImpl.java))
18+
* MyBatis (see [implementation](src/main/java/com/clevergang/dbtests/repository/impl/mybatis/MyBatisDataRepositoryImpl.java) and [mapper](src/main/resources/mybatis/mappers/DataRepositoryMapper.xml))
1319

14-
We tried to find optimal (== most readable) implementation in every framework, but comments are welcomed! There are lot of comments explaining why we chose to such implementation and some FIXMEs on places which we do not like, but which cannot be implemented differently or which we do not know how to improve...
20+
We tried to find optimal (== most readable) implementation in every framework, but comments are welcomed! There are lot of comments explaining why we chose to such implementation and some FIXMEs on places which we do not like, but which cannot be implemented differently or which we have troubles to improve...
1521

1622
## Scenarios implemented
1723

@@ -49,11 +55,47 @@ Well, we were trying to "stick with standard" in our projects so we used JPA in
4955

5056
So we dropped JPA completely, started using JDBCTemplate and discovered that we can deliver apps sooner (which was kind of surprising), they are a lot faster (thanks to effective use of DB) and much more robust... This was really relaxing and we do not plan to return to JPA at all... (yes, even for CRUD applications!)
5157

52-
This project aims to explore other options in the SQL mapping area than just JDBCTemplate. It should serve us
53-
54-
- as point of reference when deciding for SQL mapping framework
55-
- as a template of common DB usage scenarios
56-
- to document best practices of such common usages (**comments are welcomed!**)
57-
58-
59-
**Use code in the repository as you like (MIT License)**
58+
This project aims to explore other options in the SQL mapping area than just JDBCTemplate.
59+
60+
## Conclusions/Notes
61+
62+
I was able to implement all of the scenarios with all of the tested frameworks -
63+
with only difference in how comfortable or inconvenient it was. Please note that following remarks
64+
are very subjective and does not have to necessarily apply to you.
65+
66+
#### What would I choose
67+
68+
1. If project manager is ok with additional cost of a licence or the project uses one of open source databases (like PostgreSQL) then definitely go with **jOOQ**.
69+
2. If project uses Oracle, DB2, MSSQL or any other commercial database and additional cost for jOOQ licence is not acceptable, then go with **JDBCTemplate**
70+
71+
#### Subjective pros/cons of each framework
72+
73+
**JDBC Template**
74+
* Pros
75+
* Feels like you are very close to JDBC itself
76+
* Implemented all of the scenarios without bigger issues - there were no hidden surprises
77+
* Very easy batch operations
78+
* Easy setup
79+
* Cons
80+
* Methods in JDBCDataRepositoryImpl are not much readable - that's because you have to inline SQL in Java code. It would have been better if Java supported multiline strings.
81+
* Debug logging could be better
82+
83+
**jOOQ**
84+
* Pros
85+
* Very fluent, very easy to write new queries, code is very readable
86+
* Once setup it's very easy to use, excellent for simple queries
87+
* Awesome logger debug output
88+
* Cons
89+
* Paid licence for certain databases - it'll be difficult to persuade managers that it's worth it :)
90+
* Not so much usable for big queries - it's better to use native SQL (see scenario 9.)
91+
* Weird syntax for performing batch operations (in case that you do not use UpdatableRecord). But it's not a big deal...
92+
93+
94+
**MyBatis**
95+
* Pros
96+
* Writing SQL statements in XML mapper file feels good - it's easy to work with parameters.
97+
* Cons
98+
* quite a lot of files for single DAO imlementation (MyBatisDataRepositoryImpl, DataRepositoryMapper and DataRepositoryMapper.xml), though navigation is not such a big deal
99+
* at version 3.4.0 unable to work with Java8 DateTime types (LocalDate etc.), support possible through 3rd party library (mybatis-types), see build.gradle and <typeHandlers> configuration in mybatis-config.xml
100+
* can't run batch and non-batch operations in single SqlSession, but have to create completely new SqlSession instead (see configuration in DbTestsApplication class). Surprisingly, this does not necessarily mean that the batch and non-batch operations will be executed in different transactions (as we would expect), so at the end this is not a total drawback, but just inconvenience
101+
* expected that localCacheScope=STATEMENT is default MyBatis behavior, which is not... I know this is questionable drawback, but it was kind of surprise for me, see mybatis-config.xml

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ description = 'Comparison and common patterns for non-JPA SQL mapping frameworks
1717

1818
repositories {
1919
jcenter()
20+
maven { url "https://jitpack.io" }
2021
}
2122

2223
// We need Java8 for this project
@@ -27,10 +28,14 @@ tasks.withType(JavaCompile) {
2728
dependencies {
2829
compile "org.springframework.boot:spring-boot-starter-jdbc"
2930
compile "org.springframework.boot:spring-boot-starter-jooq"
31+
compile 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1'
3032
compile "org.postgresql:postgresql:9.4.1211.jre7"
3133
compile "org.apache.commons:commons-lang3:3.4"
3234
compile "org.apache.commons:commons-collections4:4.1"
3335

36+
// required so mybatis works with java8 datetime classes (LocalDate)
37+
compile 'com.github.javaplugs:mybatis-types:0.3'
38+
3439
testCompile "org.springframework.boot:spring-boot-starter-test"
3540
}
3641

sql-updates/create-script.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ CREATE TABLE Company (
1818

1919
CREATE TABLE Department (
2020
pid SERIAL PRIMARY KEY,
21-
company_pid INTEGER NOT NULL REFERENCES Company (pid) ON DELETE CASCADE,
21+
company_pid INTEGER NOT NULL REFERENCES Company (pid) ON DELETE NO ACTION,
2222
name TEXT NOT NULL
2323
);
2424

2525
CREATE TABLE Employee (
2626
pid SERIAL PRIMARY KEY,
27-
department_pid INTEGER NOT NULL REFERENCES Department (pid) ON DELETE CASCADE,
27+
department_pid INTEGER NOT NULL REFERENCES Department (pid) ON DELETE NO ACTION,
2828
name TEXT NOT NULL,
2929
surname TEXT NOT NULL,
3030
email TEXT,
@@ -40,8 +40,8 @@ CREATE TABLE Project (
4040
);
4141

4242
CREATE TABLE ProjectEmployee (
43-
project_pid INTEGER REFERENCES Project,
44-
employee_pid INTEGER REFERENCES Employee,
43+
project_pid INTEGER REFERENCES Project ON DELETE NO ACTION,
44+
employee_pid INTEGER REFERENCES Employee ON DELETE NO ACTION,
4545
PRIMARY KEY (project_pid, employee_pid)
4646
);
4747

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
package com.clevergang.dbtests;
22

3+
import org.apache.ibatis.session.ExecutorType;
4+
import org.apache.ibatis.session.SqlSession;
5+
import org.apache.ibatis.session.SqlSessionFactory;
36
import org.jooq.conf.RenderNameStyle;
47
import org.jooq.conf.Settings;
58
import org.jooq.conf.StatementType;
9+
import org.mybatis.spring.SqlSessionFactoryBean;
10+
import org.mybatis.spring.SqlSessionTemplate;
611
import org.springframework.beans.factory.annotation.Qualifier;
712
import org.springframework.boot.SpringApplication;
813
import org.springframework.boot.autoconfigure.SpringBootApplication;
914
import org.springframework.context.annotation.Bean;
1015
import org.springframework.context.annotation.Configuration;
1116
import org.springframework.context.annotation.Primary;
17+
import org.springframework.core.io.ClassPathResource;
18+
19+
import javax.sql.DataSource;
1220

1321
/**
1422
* This whole thing is based on Spring Boot as we plan to use these frameworks with SpringBoot
@@ -17,6 +25,10 @@
1725
@Configuration
1826
public class DbTestsApplication {
1927

28+
/**
29+
* JOOQ CONFIGURATIONS
30+
**/
31+
2032
@Bean
2133
@Primary
2234
public Settings jooqSettings() {
@@ -35,6 +47,37 @@ public Settings jooqStaticStatementSettings() {
3547
return ret;
3648
}
3749

50+
/**
51+
* MYBATIS CONFIGURATIONS
52+
**/
53+
54+
@Bean
55+
@Primary
56+
@SuppressWarnings("SpringJavaAutowiringInspection")
57+
public SqlSession myBatisDefaultSession(SqlSessionFactory sqlSessionFactory) {
58+
return new SqlSessionTemplate(sqlSessionFactory);
59+
}
60+
61+
@Bean
62+
@Qualifier("batch-operations")
63+
@SuppressWarnings("SpringJavaAutowiringInspection")
64+
public SqlSession myBatisBatchOperationstSession(DataSource dataSource) throws Exception {
65+
/*
66+
NOTE: Unfortunately, in MyBatis it's not possible to execute batch and non-batch operations in single SqlSession.
67+
To support this scenario, we have to create completely new SqlSessionFactoryBean and completely new
68+
SqlSession. Surprisingly, this does not necessarily mean that the batch and non-batch operations will be
69+
executed in different transactions (as we would expect) - we tested this configuration using scenario 8.
70+
and it turned out that the bot non-batch and batch operations were run using same connection and in same transaction.
71+
I guess this has something to do with how connection is obtained by MyBatis from dataSource...
72+
*/
73+
74+
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
75+
sqlSessionFactoryBean.setDataSource(dataSource);
76+
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
77+
78+
return new SqlSessionTemplate(sqlSessionFactoryBean.getObject(), ExecutorType.BATCH);
79+
}
80+
3881
public static void main(String[] args) {
3982
SpringApplication.run(DbTestsApplication.class, args);
4083
}

0 commit comments

Comments
 (0)