Refactor to use composition instead of inheritance

- Update JDBC4Connection to implement Connection directly
- JDBC4Connection holds LimboConnection as a member field
This commit is contained in:
김선우 2025-02-07 15:37:37 +09:00
parent 400dd6dd42
commit 7409779be7
7 changed files with 48 additions and 74 deletions

View file

@ -4,15 +4,17 @@ The Limbo JDBC driver is a library for accessing and creating Limbo database fil
## Project Status
The project is actively developed. Feel free to open issues and contribute.
The project is actively developed. Feel free to open issues and contribute.
To view related works, visit this [issue](https://github.com/tursodatabase/limbo/issues/615).
## How to use
## How to use
Currently, we have not published to the maven central. Instead, you can locally build the jar and deploy it to maven local to use it.
Currently, we have not published to the maven central. Instead, you can locally build the jar and deploy it to
maven local to use it.
### Build jar and publish to maven local
### Build jar and publish to maven local
```shell
$ cd bindings/java
@ -23,44 +25,16 @@ $ make macos_x86
$ make publish_local
```
Now you can use the dependency as follows:
Now you can use the dependency as follows:
```kotlin
dependencies {
implementation("org.github.tursodatabase:limbo:0.0.1-SNAPSHOT")
implementation("org.github.tursodatabase:limbo:0.0.1-SNAPSHOT")
}
```
## Development
## Code style
### How to Run Tests
To run tests, use the following command:
```shell
$ make test
```
### Code Formatting
To unify Java's formatting style, we use Spotless. To apply the formatting style, run:
```shell
$ make lint_apply
```
To apply the formatting style for Rust, run the following command:
```shell
$ cargo fmt
```
## Concepts
Note that this project is actively developed, so the concepts might change in the future.
- `LimboDB` represents a Limbo database.
- `LimboConnection` represents a connection to `LimboDB`. Multiple `LimboConnections` can be created on the same
`LimboDB`.
- `LimboStatement` represents a Limbo database statement. Multiple `LimboStatements` can be created on the same
`LimboConnection`.
- `LimboResultSet` represents the result of `LimboStatement` execution. It is one-to-one mapped to `LimboStatement`.
- Favor composition over inheritance. For example, `JDBC4Connection` doesn't implement `LimboConnection`. Instead,
it includes `LimboConnection` as a field. This approach allows us to preserve the characteristics of Limbo using
`LimboConnection` easily while maintaining interoperability with the Java world using `JDBC4Connection`.

View file

@ -5,7 +5,6 @@ import java.util.Locale;
import java.util.Properties;
import org.github.tursodatabase.annotations.Nullable;
import org.github.tursodatabase.annotations.SkipNullableCheck;
import org.github.tursodatabase.core.LimboConnection;
import org.github.tursodatabase.jdbc4.JDBC4Connection;
import org.github.tursodatabase.utils.Logger;
import org.github.tursodatabase.utils.LoggerFactory;
@ -24,7 +23,7 @@ public class JDBC implements Driver {
}
@Nullable
public static LimboConnection createConnection(String url, Properties properties)
public static JDBC4Connection createConnection(String url, Properties properties)
throws SQLException {
if (!isValidURL(url)) return null;

View file

@ -2,7 +2,6 @@ package org.github.tursodatabase.core;
import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
@ -11,7 +10,7 @@ import org.github.tursodatabase.utils.LimboExceptionUtils;
import org.github.tursodatabase.utils.Logger;
import org.github.tursodatabase.utils.LoggerFactory;
public abstract class LimboConnection implements Connection {
public class LimboConnection {
private static final Logger logger = LoggerFactory.getLogger(LimboConnection.class);
private final long connectionPtr;
@ -38,11 +37,10 @@ public abstract class LimboConnection implements Connection {
return LimboDBFactory.open(url, filePath, properties);
}
protected void checkOpen() throws SQLException {
public void checkOpen() throws SQLException {
if (isClosed()) throw new SQLException("database connection closed");
}
@Override
public void close() throws SQLException {
if (isClosed()) {
return;
@ -53,7 +51,6 @@ public abstract class LimboConnection implements Connection {
private native void _close(long connectionPtr);
@Override
public boolean isClosed() throws SQLException {
return closed;
}
@ -80,14 +77,7 @@ public abstract class LimboConnection implements Connection {
private native long prepareUtf8(long connectionPtr, byte[] sqlUtf8) throws SQLException;
/** @return busy timeout in milliseconds. */
public int getBusyTimeout() {
// TODO: add support for busyTimeout
return 0;
}
// TODO: check whether this is still valid for limbo
/**
* Checks whether the type, concurrency, and holdability settings for a {@link ResultSet} are
* supported by the SQLite interface. Supported settings are:
@ -102,7 +92,7 @@ public abstract class LimboConnection implements Connection {
* @param resultSetConcurrency the concurrency setting.
* @param resultSetHoldability the holdability setting.
*/
protected void checkCursor(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
public void checkCursor(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
throw new SQLException("SQLite only supports TYPE_FORWARD_ONLY cursors");
@ -115,10 +105,6 @@ public abstract class LimboConnection implements Connection {
}
}
public void setBusyTimeout(int busyTimeout) {
// TODO: add support for busy timeout
}
/**
* Throws formatted SQLException with error code and message.
*

View file

@ -7,15 +7,22 @@ import java.util.Properties;
import java.util.concurrent.Executor;
import org.github.tursodatabase.annotations.SkipNullableCheck;
import org.github.tursodatabase.core.LimboConnection;
import org.github.tursodatabase.core.LimboStatement;
public class JDBC4Connection extends LimboConnection {
public class JDBC4Connection implements Connection {
private final LimboConnection connection;
public JDBC4Connection(String url, String filePath) throws SQLException {
super(url, filePath);
this.connection = new LimboConnection(url, filePath);
}
public JDBC4Connection(String url, String filePath, Properties properties) throws SQLException {
super(url, filePath, properties);
this.connection = new LimboConnection(url, filePath, properties);
}
public LimboStatement prepare(String sql) throws SQLException {
return connection.prepare(sql);
}
@Override
@ -33,8 +40,8 @@ public class JDBC4Connection extends LimboConnection {
@Override
public Statement createStatement(
int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
checkOpen();
checkCursor(resultSetType, resultSetConcurrency, resultSetHoldability);
connection.checkOpen();
connection.checkCursor(resultSetType, resultSetConcurrency, resultSetHoldability);
return new JDBC4Statement(this);
}
@ -81,12 +88,12 @@ public class JDBC4Connection extends LimboConnection {
@Override
public void close() throws SQLException {
super.close();
connection.close();
}
@Override
public boolean isClosed() throws SQLException {
return super.isClosed();
return connection.isClosed();
}
@Override
@ -351,4 +358,14 @@ public class JDBC4Connection extends LimboConnection {
// TODO
return false;
}
public void setBusyTimeout(int busyTimeout) {
// TODO: add support for busy timeout
}
/** @return busy timeout in milliseconds. */
public int getBusyTimeout() {
// TODO: add support for busyTimeout
return 0;
}
}

View file

@ -23,13 +23,12 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import org.github.tursodatabase.annotations.SkipNullableCheck;
import org.github.tursodatabase.core.LimboConnection;
public class JDBC4PreparedStatement extends JDBC4Statement implements PreparedStatement {
private final String sql;
public JDBC4PreparedStatement(LimboConnection connection, String sql) throws SQLException {
public JDBC4PreparedStatement(JDBC4Connection connection, String sql) throws SQLException {
super(connection);
this.sql = sql;

View file

@ -10,13 +10,12 @@ import java.sql.Statement;
import java.util.concurrent.locks.ReentrantLock;
import org.github.tursodatabase.annotations.Nullable;
import org.github.tursodatabase.annotations.SkipNullableCheck;
import org.github.tursodatabase.core.LimboConnection;
import org.github.tursodatabase.core.LimboResultSet;
import org.github.tursodatabase.core.LimboStatement;
public class JDBC4Statement implements Statement {
private final LimboConnection connection;
private final JDBC4Connection connection;
@Nullable protected LimboStatement statement = null;
// Because JDBC4Statement has different life cycle in compared to LimboStatement, let's use this
@ -34,7 +33,7 @@ public class JDBC4Statement implements Statement {
private ReentrantLock connectionLock = new ReentrantLock();
public JDBC4Statement(LimboConnection connection) {
public JDBC4Statement(JDBC4Connection connection) {
this(
connection,
ResultSet.TYPE_FORWARD_ONLY,
@ -43,7 +42,7 @@ public class JDBC4Statement implements Statement {
}
public JDBC4Statement(
LimboConnection connection,
JDBC4Connection connection,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability) {

View file

@ -6,21 +6,21 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import org.github.tursodatabase.core.LimboConnection;
import org.github.tursodatabase.jdbc4.JDBC4Connection;
import org.junit.jupiter.api.Test;
class JDBCTest {
@Test
void null_is_returned_when_invalid_url_is_passed() throws Exception {
LimboConnection connection = JDBC.createConnection("jdbc:invalid:xxx", new Properties());
JDBC4Connection connection = JDBC.createConnection("jdbc:invalid:xxx", new Properties());
assertThat(connection).isNull();
}
@Test
void non_null_connection_is_returned_when_valid_url_is_passed() throws Exception {
String fileUrl = TestUtils.createTempFile();
LimboConnection connection = JDBC.createConnection("jdbc:sqlite:" + fileUrl, new Properties());
JDBC4Connection connection = JDBC.createConnection("jdbc:sqlite:" + fileUrl, new Properties());
assertThat(connection).isNotNull();
}