diff --git a/jooq-spring/.classpath b/jooq-spring/.classpath new file mode 100644 index 0000000000..e43402fa4f --- /dev/null +++ b/jooq-spring/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jooq-spring/.project b/jooq-spring/.project new file mode 100644 index 0000000000..3c3f3d602b --- /dev/null +++ b/jooq-spring/.project @@ -0,0 +1,23 @@ + + + jooq-spring + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/jooq-spring/pom.xml b/jooq-spring/pom.xml new file mode 100644 index 0000000000..80eba86164 --- /dev/null +++ b/jooq-spring/pom.xml @@ -0,0 +1,159 @@ + + 4.0.0 + com.baeldung + jooq-spring + 0.0.1-SNAPSHOT + + + 3.7.3 + 1.4.191 + 2.4.4 + 4.2.5.RELEASE + 1.7.18 + 1.1.3 + 4.12 + + + + + + org.jooq + jooq + ${org.jooq.version} + + + + + com.h2database + h2 + ${com.h2database.version} + + + com.zaxxer + HikariCP + ${com.zaxxer.HikariCP.version} + + + + + org.springframework + spring-context + ${org.springframework.version} + + + org.springframework + spring-jdbc + ${org.springframework.version} + + + + + org.slf4j + slf4j-api + ${org.slf4j.version} + runtime + + + ch.qos.logback + logback-classic + ${ch.qos.logback.version} + runtime + + + + + junit + junit + ${junit.version} + test + + + org.springframework + spring-test + ${org.springframework.version} + test + + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + read-project-properties + + + + src/main/resources/intro_config.properties + + + + + + + + org.codehaus.mojo + sql-maven-plugin + 1.5 + + + initialize + + execute + + + ${db.driver} + ${db.url} + ${db.username} + ${db.password} + + src/main/resources/intro_schema.sql + + + + + + + com.h2database + h2 + ${com.h2database.version} + + + + + + org.jooq + jooq-codegen-maven + ${org.jooq.version} + + + generate-sources + + generate + + + + ${db.driver} + ${db.url} + ${db.username} + ${db.password} + + + + com.baeldung.jooq.introduction.db + src/test/java + + + + + + + + + \ No newline at end of file diff --git a/jooq-spring/src/main/resources/intro_config.properties b/jooq-spring/src/main/resources/intro_config.properties new file mode 100644 index 0000000000..3275089585 --- /dev/null +++ b/jooq-spring/src/main/resources/intro_config.properties @@ -0,0 +1,8 @@ +#Database Configuration +db.driver=org.h2.Driver +db.url=jdbc:h2:~/jooq +db.username=sa +db.password= + +#SQL Dialect +jooq.sql.dialect=H2 \ No newline at end of file diff --git a/jooq-spring/src/main/resources/intro_schema.sql b/jooq-spring/src/main/resources/intro_schema.sql new file mode 100644 index 0000000000..0d4bd26235 --- /dev/null +++ b/jooq-spring/src/main/resources/intro_schema.sql @@ -0,0 +1,34 @@ +DROP TABLE IF EXISTS author_book, author, book; + +CREATE TABLE author ( + id INT NOT NULL PRIMARY KEY, + first_name VARCHAR(50), + last_name VARCHAR(50) NOT NULL +); + +CREATE TABLE book ( + id INT NOT NULL PRIMARY KEY, + title VARCHAR(100) NOT NULL +); + +CREATE TABLE author_book ( + author_id INT NOT NULL, + book_id INT NOT NULL, + + PRIMARY KEY (author_id, book_id), + CONSTRAINT fk_ab_author FOREIGN KEY (author_id) REFERENCES author (id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT fk_ab_book FOREIGN KEY (book_id) REFERENCES book (id) +); + +INSERT INTO author VALUES + (1, 'Kathy', 'Sierra'), + (2, 'Bert', 'Bates'), + (3, 'Bryan', 'Basham'); + +INSERT INTO book VALUES + (1, 'Head First Java'), + (2, 'Head First Servlets and JSP'), + (3, 'OCA/OCP Java SE 7 Programmer'); + +INSERT INTO author_book VALUES (1, 1), (1, 3), (2, 1); \ No newline at end of file diff --git a/jooq-spring/src/test/java/com/baeldung/jooq/introduction/ExceptionTranslator.java b/jooq-spring/src/test/java/com/baeldung/jooq/introduction/ExceptionTranslator.java new file mode 100644 index 0000000000..7bee21f077 --- /dev/null +++ b/jooq-spring/src/test/java/com/baeldung/jooq/introduction/ExceptionTranslator.java @@ -0,0 +1,19 @@ +package com.baeldung.jooq.introduction; + +import org.jooq.ExecuteContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DefaultExecuteListener; + +import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; +import org.springframework.jdbc.support.SQLExceptionTranslator; + +public class ExceptionTranslator extends DefaultExecuteListener { + private static final long serialVersionUID = 649359748808106775L; + + @Override + public void exception(ExecuteContext context) { + SQLDialect dialect = context.configuration().dialect(); + SQLExceptionTranslator translator = new SQLErrorCodeSQLExceptionTranslator(dialect.name()); + context.exception(translator.translate("Access database using jOOQ", context.sql(), context.sqlException())); + } +} \ No newline at end of file diff --git a/jooq-spring/src/test/java/com/baeldung/jooq/introduction/PersistenceContext.java b/jooq-spring/src/test/java/com/baeldung/jooq/introduction/PersistenceContext.java new file mode 100644 index 0000000000..4bcf3a527e --- /dev/null +++ b/jooq-spring/src/test/java/com/baeldung/jooq/introduction/PersistenceContext.java @@ -0,0 +1,79 @@ +package com.baeldung.jooq.introduction; + +import javax.sql.DataSource; +import com.zaxxer.hikari.HikariDataSource; + +import org.jooq.SQLDialect; +import org.jooq.impl.DataSourceConnectionProvider; +import org.jooq.impl.DefaultConfiguration; +import org.jooq.impl.DefaultDSLContext; +import org.jooq.impl.DefaultExecuteListenerProvider; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@ComponentScan({ "com.baeldung.jooq.introduction.db.public_.tables" }) +@EnableTransactionManagement +@PropertySource("classpath:intro_config.properties") +public class PersistenceContext { + @Autowired + private Environment environment; + + @Bean + public DataSource dataSource() { + HikariDataSource dataSource = new HikariDataSource(); + + dataSource.setDriverClassName(environment.getRequiredProperty("db.driver")); + dataSource.setJdbcUrl(environment.getRequiredProperty("db.url")); + dataSource.setUsername(environment.getRequiredProperty("db.username")); + dataSource.setPassword(environment.getRequiredProperty("db.password")); + + return dataSource; + } + + @Bean + public TransactionAwareDataSourceProxy transactionAwareDataSource() { + return new TransactionAwareDataSourceProxy(dataSource()); + } + + @Bean + public DataSourceTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource()); + } + + @Bean + public DataSourceConnectionProvider connectionProvider() { + return new DataSourceConnectionProvider(transactionAwareDataSource()); + } + + @Bean + public ExceptionTranslator exceptionTransformer() { + return new ExceptionTranslator(); + } + + @Bean + public DefaultDSLContext dsl() { + return new DefaultDSLContext(configuration()); + } + + @Bean + public DefaultConfiguration configuration() { + DefaultConfiguration jooqConfiguration = new DefaultConfiguration(); + jooqConfiguration.set(connectionProvider()); + jooqConfiguration.set(new DefaultExecuteListenerProvider(exceptionTransformer())); + + String sqlDialectName = environment.getRequiredProperty("jooq.sql.dialect"); + SQLDialect dialect = SQLDialect.valueOf(sqlDialectName); + jooqConfiguration.set(dialect); + + return jooqConfiguration; + } +} \ No newline at end of file diff --git a/jooq-spring/src/test/java/com/baeldung/jooq/introduction/QueryTest.java b/jooq-spring/src/test/java/com/baeldung/jooq/introduction/QueryTest.java new file mode 100644 index 0000000000..34be23bf2a --- /dev/null +++ b/jooq-spring/src/test/java/com/baeldung/jooq/introduction/QueryTest.java @@ -0,0 +1,87 @@ +package com.baeldung.jooq.introduction; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.jooq.DSLContext; +import org.jooq.Record3; +import org.jooq.Result; +import org.jooq.impl.DSL; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.jooq.introduction.db.public_.tables.Author; +import com.baeldung.jooq.introduction.db.public_.tables.AuthorBook; +import com.baeldung.jooq.introduction.db.public_.tables.Book; + +@ContextConfiguration(classes = PersistenceContext.class) +@Transactional(transactionManager = "transactionManager") +@RunWith(SpringJUnit4ClassRunner.class) +public class QueryTest { + @Autowired + private DSLContext create; + + Author author = Author.AUTHOR; + Book book = Book.BOOK; + AuthorBook authorBook = AuthorBook.AUTHOR_BOOK; + + @Test + public void givenValidData_whenInserting_thenSucceed() { + create.insertInto(author).set(author.ID, 4).set(author.FIRST_NAME, "Herbert").set(author.LAST_NAME, "Schildt").execute(); + create.insertInto(book).set(book.ID, 4).set(book.TITLE, "A Beginner's Guide").execute(); + create.insertInto(authorBook).set(authorBook.AUTHOR_ID, 4).set(authorBook.BOOK_ID, 4).execute(); + Result> result = create.select(author.ID, author.LAST_NAME, DSL.count()).from(author).join(authorBook).on(author.ID.equal(authorBook.AUTHOR_ID)).join(book).on(authorBook.BOOK_ID.equal(book.ID)) + .groupBy(author.LAST_NAME).fetch(); + + assertEquals(3, result.size()); + assertEquals("Sierra", result.getValue(0, author.LAST_NAME)); + assertEquals(Integer.valueOf(2), result.getValue(0, DSL.count())); + assertEquals("Schildt", result.getValue(2, author.LAST_NAME)); + assertEquals(Integer.valueOf(1), result.getValue(2, DSL.count())); + } + + @Test(expected = DataAccessException.class) + public void givenInvalidData_whenInserting_thenFail() { + create.insertInto(authorBook).set(authorBook.AUTHOR_ID, 4).set(authorBook.BOOK_ID, 5).execute(); + } + + @Test + public void givenValidData_whenUpdating_thenSucceed() { + create.update(author).set(author.LAST_NAME, "Baeldung").where(author.ID.equal(3)).execute(); + create.update(book).set(book.TITLE, "Building your REST API with Spring").where(book.ID.equal(3)).execute(); + create.insertInto(authorBook).set(authorBook.AUTHOR_ID, 3).set(authorBook.BOOK_ID, 3).execute(); + Result> result = create.select(author.ID, author.LAST_NAME, book.TITLE).from(author).join(authorBook).on(author.ID.equal(authorBook.AUTHOR_ID)).join(book).on(authorBook.BOOK_ID.equal(book.ID)).where(author.ID.equal(3)) + .fetch(); + + assertEquals(1, result.size()); + assertEquals(Integer.valueOf(3), result.getValue(0, author.ID)); + assertEquals("Baeldung", result.getValue(0, author.LAST_NAME)); + assertEquals("Building your REST API with Spring", result.getValue(0, book.TITLE)); + } + + @Test(expected = DataAccessException.class) + public void givenInvalidData_whenUpdating_thenFail() { + create.update(authorBook).set(authorBook.AUTHOR_ID, 4).set(authorBook.BOOK_ID, 5).execute(); + } + + @Test + public void givenValidData_whenDeleting_thenSucceed() { + create.delete(author).where(author.ID.lt(3)).execute(); + Result> result = create.select(author.ID, author.FIRST_NAME, author.LAST_NAME).from(author).fetch(); + + assertEquals(1, result.size()); + assertEquals("Bryan", result.getValue(0, author.FIRST_NAME)); + assertEquals("Basham", result.getValue(0, author.LAST_NAME)); + } + + @Test(expected = DataAccessException.class) + public void givenInvalidData_whenDeleting_thenFail() { + create.delete(book).where(book.ID.equal(1)).execute(); + } +} \ No newline at end of file