Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 130 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

**Superb JDBC** is an object-oriented wrapper of JDBC API that simplifies work with relational databases.

**Key Features:**
- Convenient use through clear DBMS abstractions (Queries, Transactions, etc.).
- Close attention to the reliable (encapsulation of closing Connections, ResultSets, etc.).
- Close attention to the performance (encapsulation of PreparedStatements, etc.).
- SQL and nothing more.
- Zero dependencies.
- MIT License.
**Principles:**
- Convenient use through clear DBMS abstractions (Queries, Transactions, etc.)
- Close attention to the reliable
- Close attention to the performance
- SQL and nothing more
- Zero dependencies
- MIT License

## Quick Start

Expand All @@ -31,12 +31,131 @@ Rdbms rdbms = new RealRdbms(dataSource);
rdbms.change("INSERT INTO books(title) VALUES ('Clean Code')").apply();
```

## Use cases
## Abstractions

### Simple - select
### Rdbms

Interface `Rdbms` combines interfaces: `Queries`, `Changes`, `Batches`, `Transactions` (factories of objects `Query`,
`Change`, `Batch` and `Transaction`).

This separation of interfaces allows us to not violate the _Liskov's Substitution Principle_ in places where we
want to provide partial functionality.

Note that extracting interfaces also allows us to use doubles (Fake, Stub, etc.) to write tests.

For real interaction with the RDBMS, the `RealRdbms` class is used, an example of creating an object of the corresponding class:
```java
List<String> titles = rdbms
Rdbms rdbms = new RealRdbms(dataSource);
```

### Query

Interface `Query` is a request to which RDBMS returns a result set, which must then be processed in some way.

#### Simple example

```java
List<String> titles = queries
.query("SELECT title FROM books")
.executeWith(new StringListHandler("title"));
.executeWith(new ColumnToListRsh<>(new StringColumn("title")));
```

#### Using parameters

```java
List<String> titles = queries
.query("SELECT title FROM books WHERE title LIKE ?",
new StringArgument("Clean%"))
.executeWith(new ColumnToListRsh<>(new StringColumn("title")));
```

Implementations of the `Argument` interface are used to define arguments. `Superb-jdbc` has several commonly used
implementations of the corresponding interface, but you can always implement your own implementation.
This approach makes `superb-jdbc` open to extension (_Open–closed principle_).

It is also worth paying attention to the fact that the arguments are specified directly in the place where the
parameterized query is defined, which reduces the likelihood of making a mistake (unlike `jdbc`, where we first
define the query and then set the parameter values).

#### Building a query

```java
Query titlesQuery = queries.query("SELECT title FROM books");
titlesQuery.append(" WHERE title LIKE ?", new StringArgument("Clean%"));
titlesQuery.append(" LIMIT ?", new IntArgument(10));
List<String> titles = titlesQuery
.executeWith(new ColumnToListRsh<>(new StringColumn("title")));
```

This approach can be useful, for example, when we have a web form and, depending on the fields filled in it, we want to
be able to change the structure of our query (include or exclude some parts of the query).

### Change

The `Change` interface is a request to change the database (_DDL or DML_).

#### Simple example

```java
changes
.change("INSERT INTO books(title) VALUES ('Clean Code')")
.apply();
```

#### Using parameters

```java
changes
.change("INSERT INTO books(title) VALUES (?)", new StringArgument("Clean Code"))
.apply();
```

### Batch

The `Batch` interface is designed to efficiently insert a batch of data.

#### Simple example

```java
try (Batch batch = batches.batch("INSERT INTO books(title) VALUES (?)")) {
batch.put(new StringArgument("Clean Code"));
batch.put(new StringArgument("Code Complete"));
batch.put(new StringArgument("Effective Java"));
batch.apply();
}
```

### Transaction

The `Transaction` interface is a transaction within which you can combine several queries to the RDBMS.

#### Simple example

```java
try (Transaction transaction = transactions.transaction()) {
transaction.change("INSERT INTO books(title) VALUES ('Clean Code')").apply();
transaction.change("INSERT INTO books(title) VALUES ('Code Complete')").apply();
transaction.change("INSERT INTO books(title) VALUES ('Effective Java')").apply();
transaction.commit();
}
```

#### Setting the transaction isolation level

```java
try (Transaction transaction = transactions.transaction(1)) {
// some code
}
```

#### Using savepoints
```java
try (Transaction transaction = transactions.transaction()) {
transaction.change("INSERT INTO books(title) VALUES ('Clean Code')").apply();
transaction.setSavepoint("MySavepoint");
transaction.change("INSERT INTO books(title) VALUES ('Code Complete')").apply();
transaction.rollbackTo("MySavepoint");
transaction.change("INSERT INTO books(title) VALUES ('Effective Java')").apply();
transaction.commit();
}
```
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ SOFTWARE.

<groupId>com.nmalygin</groupId>
<artifactId>superb-jdbc</artifactId>
<version>0.0.4-SNAPSHOT</version>
<version>0.0.5-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Superb JDBC</name>
Expand Down
148 changes: 147 additions & 1 deletion src/test/java/com/nmalygin/superb/jdbc/real/ReadmeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@

package com.nmalygin.superb.jdbc.real;

import com.nmalygin.superb.jdbc.api.Rdbms;
import com.nmalygin.superb.jdbc.api.*;
import com.nmalygin.superb.jdbc.api.arguments.IntArgument;
import com.nmalygin.superb.jdbc.api.arguments.ObjectArgument;
import com.nmalygin.superb.jdbc.api.arguments.StringArgument;
import com.nmalygin.superb.jdbc.api.handlers.ColumnToListRsh;
import com.nmalygin.superb.jdbc.api.handlers.columns.StringColumn;
import com.nmalygin.superb.jdbc.real.testdb.BooksTable;
import com.nmalygin.superb.jdbc.real.testdb.DataSourceBooksTable;
import com.nmalygin.superb.jdbc.real.testdb.H2DataSource;
Expand All @@ -33,6 +38,8 @@

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;

Expand All @@ -49,4 +56,143 @@ void quickStart() throws SQLException {

assertEquals(1, booksTable.books().size());
}

@Test
void simpleSelect() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);
booksTable.insert(UUID.randomUUID(), "Clean Code");

Queries queries = new RealRdbms(dataSource);
List<String> titles = queries
.query("SELECT title FROM books")
.executeWith(new ColumnToListRsh<>(new StringColumn("title")));

assertEquals(1, titles.size());
assertEquals("Clean Code", titles.get(0));
}

@Test
void selectWithParams() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);
booksTable.insert(UUID.randomUUID(), "Clean Code");

Queries queries = new RealRdbms(dataSource);
List<String> titles = queries
.query(
"SELECT title FROM books WHERE title LIKE ?",
new StringArgument("Clean%")
)
.executeWith(new ColumnToListRsh<>(new StringColumn("title")));

assertEquals(1, titles.size());
assertEquals("Clean Code", titles.get(0));
}

@Test
void buildingSelect() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);
booksTable.insert(UUID.randomUUID(), "Clean Code");

Queries queries = new RealRdbms(dataSource);

Query titlesQuery = queries.query("SELECT title FROM books");
titlesQuery.append(" WHERE title LIKE ?", new StringArgument("Clean%"));
titlesQuery.append(" LIMIT ?", new IntArgument(10));
List<String> titles = titlesQuery.executeWith(new ColumnToListRsh<>(new StringColumn("title")));

assertEquals(1, titles.size());
assertEquals("Clean Code", titles.get(0));
}

@Test
void changeSimpleExample() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);

Changes changes = new RealRdbms(dataSource);

changes
.change("INSERT INTO books(title) VALUES ('Clean Code')")
.apply();

assertEquals(1, booksTable.books().size());
}

@Test
void changeWithParams() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);

Changes changes = new RealRdbms(dataSource);

changes
.change("INSERT INTO books(title) VALUES (?)", new StringArgument("Clean Code"))
.apply();

assertEquals(1, booksTable.books().size());
}

@Test
void batchSimpleExample() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);

Batches batches = new RealRdbms(dataSource);

try (Batch batch = batches.batch("INSERT INTO books(title) VALUES (?)")) {
batch.put(new StringArgument("Clean Code"));
batch.put(new StringArgument("Code Complete"));
batch.put(new StringArgument("Effective Java"));
batch.apply();
}

assertEquals(3, booksTable.books().size());
}

@Test
void transactionSimpleExample() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);

Transactions transactions = new RealRdbms(dataSource);

try (Transaction transaction = transactions.transaction()) {
transaction.change("INSERT INTO books(title) VALUES ('Clean Code')").apply();
transaction.change("INSERT INTO books(title) VALUES ('Code Complete')").apply();
transaction.change("INSERT INTO books(title) VALUES ('Effective Java')").apply();
transaction.commit();
}

assertEquals(3, booksTable.books().size());
}

@Test
void transactionUsingSavepoints() throws SQLException {
final DataSource dataSource = new H2DataSource();
new LibraryDB(dataSource).init();
final BooksTable booksTable = new DataSourceBooksTable(dataSource);

Transactions transactions = new RealRdbms(dataSource);

try (Transaction transaction = transactions.transaction()) {
transaction.change("INSERT INTO books(title) VALUES ('Clean Code')").apply();
transaction.setSavepoint("MySavepoint");
transaction.change("INSERT INTO books(title) VALUES ('Code Complete')").apply();
transaction.rollbackTo("MySavepoint");
transaction.change("INSERT INTO books(title) VALUES ('Effective Java')").apply();
transaction.commit();
}

assertEquals(3, booksTable.books().size());
}
}
Loading