Skip to content

Commit

Permalink
extend the logic of the RecordBase + manual test (iluwatar#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergejsvisockis committed Apr 15, 2024
1 parent fe908cc commit 29ed0f9
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 30 deletions.
4 changes: 2 additions & 2 deletions active-record/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ understand and maintain codebases.

## Tutorials

* [Panache – Active Record Pattern](https://thorben-janssen.com/panache-active-record-pattern/)
* [Active Record pattern in Java](https://objsql.hashnode.dev/active-record-pattern-in-java)
* [Active Record](https://www.martinfowler.com/eaaCatalog/activeRecord.html/)
* [Overview of the Active Record Pattern](https://blog.savetchuk.com/overview-of-the-active-record-pattern)

## Consequences

Expand Down
72 changes: 72 additions & 0 deletions active-record/src/main/java/com/iluwatar/activerecord/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.iluwatar.activerecord;

import java.sql.SQLException;
import javax.sql.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.h2.jdbcx.JdbcDataSource;

@Slf4j
public class App {

private static final String DB_URL = "jdbc:h2:mem:dao;DB_CLOSE_DELAY=-1";

private static final String CREATE_SCHEMA_SQL = "CREATE TABLE customer\n"
+ "(\n"
+ " id BIGINT NOT NULL,\n"
+ " customer_number VARCHAR(15) NOT NULL,\n"
+ " first_name VARCHAR(45) NOT NULL,\n"
+ " last_name VARCHAR(45) NOT NULL,\n"
+ " CONSTRAINT customer_pkey PRIMARY KEY (id)\n"
+ ");\n"
+ "\n"
+ "CREATE TABLE \"order\"\n"
+ "(\n"
+ " id BIGINT NOT NULL,\n"
+ " order_number VARCHAR(15) NOT NULL,\n"
+ " customer_id BIGINT NOT NULL,\n"
+ " CONSTRAINT order_pkey PRIMARY KEY (id),\n"
+ " CONSTRAINT customer_id_fk FOREIGN KEY (customer_id) REFERENCES customer (id)\n"
+ ")";


public static void main(final String[] args) throws Exception {
final var dataSource = createDataSource();
createSchema(dataSource);
RecordBase.setDataSource(dataSource);
executeOperation();
}

private static void executeOperation() {
LOGGER.info("saving the customer data...");
Customer customer = new Customer();
customer.setId(1L);
customer.setCustomerNumber("C123");
customer.setFirstName("John");
customer.setLastName("Smith");

Order order = new Order();
order.setId(1L);
order.setOrderNumber("O123");

// customer.addOrder(order);
customer.save();

LOGGER.info("The customer data by ID={}", customer.findById(1L));

LOGGER.info("find all the customers={}", customer.findAll());
}

private static void createSchema(DataSource dataSource) throws SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(CREATE_SCHEMA_SQL);
}
}

private static DataSource createDataSource() {
var dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
return dataSource;
}

}
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
package com.iluwatar.activerecord;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
public class Customer extends RecordBase {
@ToString
public class Customer extends RecordBase<Customer> {

private Long id;
private String customerNumber;
private String firstName;
private String lastName;
private List<Order> orders;

public Customer(DataSource dataSource) {
super(dataSource);
}

public Customer findByNumber(String customerNumber) {
// TODO
return null;
// return new Customer();
}

public void addOrder(Order order) {
orders.add(order);
}

@Override
Expand All @@ -32,7 +34,15 @@ protected String getTableName() {
}

@Override
protected void setFieldsFromResultSet(ResultSet rs) {
protected void setFieldsFromResultSet(ResultSet rs) throws SQLException {
this.id = rs.getLong("id");
this.customerNumber = rs.getString("customer_number");
this.firstName = rs.getString("first_name");
this.lastName = rs.getString("last_name");
}

@Override
protected void setPreparedStatementParams(PreparedStatement pstmt) throws SQLException {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
import java.util.List;
import javax.sql.DataSource;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

@RequiredArgsConstructor
public abstract class RecordBase {
public abstract class RecordBase<T extends RecordBase<?>> {

private final DataSource dataSource;
@Setter
private static DataSource dataSource;

@SuppressWarnings({"unchecked"})
private final Class<T> clazz = (Class<T>) getClass();

protected Connection getConnection() throws SQLException {
return dataSource.getConnection();
Expand All @@ -26,22 +31,22 @@ protected Connection getConnection() throws SQLException {
*/
protected abstract String getTableName();

protected abstract void setFieldsFromResultSet(ResultSet rs);
protected abstract void setFieldsFromResultSet(ResultSet rs) throws SQLException;

protected abstract void setPreparedStatementParams(PreparedStatement pstmt) throws SQLException;

/**
* Find all the records for a corresponding domain model.
*
* @param clazz domain model class.
* @param <T> domain model type.
* @return all the domain model related records.
*/
public <T extends RecordBase> List<T> findAll(Class<T> clazz) {
public List<T> findAll() {
List<T> recordList = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(constructGetByIdQuery(clazz))) {
PreparedStatement pstmt = conn.prepareStatement(constructFindAllQuery())) {
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
T record = getDeclaredClassInstance(clazz);
T record = getDeclaredClassInstance();
record.setFieldsFromResultSet(rs);
recordList.add(record);
}
Expand All @@ -57,26 +62,24 @@ public <T extends RecordBase> List<T> findAll(Class<T> clazz) {
/**
* Find a domain model by its ID.
*
* @param id domain model identifier.
* @param clazz domain model class.
* @param <T> domain model type.
* @param id domain model identifier.
* @return the domain model.
*/
public <T extends RecordBase> T findById(Long id, Class<T> clazz) {
public T findById(Long id) {
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(constructGetByIdQuery(clazz))) {
PreparedStatement pstmt = conn.prepareStatement(constructFindByIdQuery())) {
pstmt.setLong(1, id);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
T record = getDeclaredClassInstance(clazz);
T record = getDeclaredClassInstance();
record.setFieldsFromResultSet(rs);
return record;
}
return getDeclaredClassInstance(clazz);
return getDeclaredClassInstance();
}
} catch (SQLException e) {
throw new RuntimeException(
"Unable to fine a record for the following domain model : " + clazz.getName() + " by id="
"Unable to find a record for the following domain model : " + clazz.getName() + " by id="
+ id
+ " due to the data persistence error", e);
}
Expand All @@ -86,15 +89,32 @@ public <T extends RecordBase> T findById(Long id, Class<T> clazz) {
* Save the record.
*/
public void save() {
// TODO
try (Connection connection = getConnection();
// TODO
PreparedStatement pstmt = connection.prepareStatement(null,
PreparedStatement.RETURN_GENERATED_KEYS)) {

setPreparedStatementParams(pstmt);
pstmt.executeUpdate();

} catch (SQLException e) {
throw new RuntimeException(
"Unable to save the record for the following domain model : " + clazz.getName()
+ " due to the data persistence error", e);
}
}


private String constructFindAllQuery() {
return "SELECT * FROM " + getDeclaredClassInstance().getTableName();
}

private <T extends RecordBase> String constructGetByIdQuery(Class<T> clazz) {
return "SELECT * FROM " + getDeclaredClassInstance(clazz).getTableName()
private String constructFindByIdQuery() {
return "SELECT * FROM " + getDeclaredClassInstance().getTableName()
+ " WHERE id = ?";
}

private <T extends RecordBase> T getDeclaredClassInstance(Class<T> clazz) {
private T getDeclaredClassInstance() {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException |
Expand Down

0 comments on commit 29ed0f9

Please sign in to comment.