Skip to content

Commit

Permalink
improve api to work better with json generators like Jackson (#9)
Browse files Browse the repository at this point in the history
* improve our api to work better with json generators like Jackson

* commit badge

* Update version, improve readme

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
martin-jamszolik and github-actions[bot] authored Dec 28, 2022
1 parent 266f466 commit 10a74c4
Show file tree
Hide file tree
Showing 14 changed files with 135 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .github/badges/jacoco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,29 @@
[![branches](.github/badges/branches.svg)](branches.svg)

## Why?
Have you tried the latest/greatest ORM frameworks out there yet?
Have you tried the latest/greatest ORM frameworks out there yet?

There are more than I can count, each one of them makes some kind of promise to you.
Most of them want to remove you from the RDBMS layer as much as possible. Some use meta-programming
to generate the model in runtime, some are statically typed and require us to define the schema
in the application layer so that a rich DSL layer can be used.

Reflecting on this, you may find yourself needing to be close to SQL and all that
Reflecting on this, you may find yourself needing to stay close to SQL and all that
it has to offer in flexibility and transparency. Tailor each query for optimal retrieval, using
your knowledge of to put together a performant query. At the same time, have a good enough api to
help you with the boring,silly stuff. Just enough to help with the needed
insert,update and simple select statements. Find that middle ground of convention and flexibility.
How low level can we stay and still be productive with RDBMS? This little library was born from
having to work with so many frameworks, between `spring-data` and `rails` and many other
specialized libraries.
your knowledge to put together a performant query. At the same time, have a good enough api to
help you with the boring stuff. Just enough to help with the needed
insert, update and simple select statements. Find that middle ground of convention and flexibility.
How low level can we stay and still be productive with RDBMS? This little library was created as a
reflection on other frameworks I worked with `spring-data`, `rails` and the likes, among others.

## How?

Clearly, we start of with the fact that we are on the JVM. Taking advantage of one of the most
Starting of with the fact that we are on the JVM and using Java. Taking advantage of one of the most
prolific frameworks out there, which is `spring-jdbc`. An already slim, low level library.
Between `spring-jdbc` and Springs next flagship library `spring-data`,
this little project sits right in between. We don't do any meta-programming (autogenerate interfaces),
we don't generate any clever queries. Instead, we help with the most boring parts and leave the power
and flexibility and a level of complexity to you. With some good guidelines, best practices and test cases, you may find this library useful.
and flexibility and a level of complexity to you. With some good guidelines, best practices and test cases,
you may find this library useful.


## Details
Expand Down Expand Up @@ -66,7 +65,9 @@ Beyond that, extend the class and define your desired helper methods for additio
Most mapping needs can be delegated over to `PersistableRowMapper`. Only if you want to take full control of
how every column, field and foreign relationships is mapped, you will want to implement
`PersistableMapper` and go from there. For an advanced example of this, see
[ProposalMapper](src/test/java/org/viablespark/persistence/ProposalMapper.java)
[ProposalMapper](src/test/java/org/viablespark/persistence/ProposalMapper.java) This is the most laborious
part of the library, queries will need to match entities fields, so often you will have to spell out
the select statement with `column as renamed`. A tradeoff I can live with to retain fine control.


### Test Cases
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ plugins {
}

group = "org.viablespark"
version = "1.3.0"
version = "1.4.0"

repositories {
mavenCentral()
Expand Down Expand Up @@ -75,7 +75,7 @@ publishing {
create<MavenPublication>("maven") {
groupId = "org.viablespark"
artifactId = "goodenough-jdbc"
version = "1.3.0"
version = "1.4.0"

from(components["java"])
pom {
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/viablespark/persistence/BaseRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ public Optional<Key> save(E entity) throws SQLException {

public void delete(E entity) {
String sql = "DELETE FROM " + deriveEntityName(entity.getClass())
+ " WHERE " + entity.getKey().getPrimaryKey().key + "=? ";
+ " WHERE " + entity.getKey().primaryKey().getKey() + "=? ";

jdbc.update(sql, entity.getKey().getPrimaryKey().value);
jdbc.update(sql, entity.getKey().primaryKey().getValue());
}

public Optional<E> get(Key key, Class<E> cls) throws NoSuchElementException {
List<E> list = jdbc.query(
"SELECT " + WithSql.getSQLSelectClause(cls, key.getPrimaryKey().key)
"SELECT " + WithSql.getSQLSelectClause(cls, key.primaryKey().getKey())
+ " FROM " + deriveEntityName(cls)
+ " WHERE " + key.getPrimaryKey().key + "=?",
+ " WHERE " + key.primaryKey().getKey() + "=?",
new PersistableRowMapper<>(cls),
key.getPrimaryKey().value);
key.primaryKey().getValue());

Optional<E> res = list.stream().findFirst();
res.ifPresent(e -> e.setKey(key));
Expand Down
23 changes: 15 additions & 8 deletions src/main/java/org/viablespark/persistence/Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ public class Key implements Serializable {

private final Map<String,Pair<String, Long>> keys = new LinkedHashMap<>();

public Key() {
}

public Key(Pair<String, Long> primaryKey) {
keys.put(primaryKey.key, primaryKey);
keys.put(primaryKey.getKey(), primaryKey);
}

public static Key of(String key, Long value){
Expand All @@ -47,14 +50,18 @@ public Key add(String name, Long key) {
return this;
}

public int getCount() {
public int count() {
return keys.size();
}

public Collection<Pair<String, Long>> getKeys() {
return keys.values();
}

public void setKeys(Collection<Pair<String,Long>> _keys){
_keys.forEach( pair -> keys.put(pair.getKey(),pair));
}

public Pair<String, Long> getAt(int index) {
int counter=0;
for (Entry<String, Pair<String, Long>> entry : keys.entrySet()) {
Expand All @@ -71,7 +78,7 @@ public Long getKey(String name) {
if( found == null ){
throw new RuntimeException("Key " + name + " not found. Other keys?" + this);
}
return found.value;
return found.getValue();
}

public Optional<Pair<String, Long>> contains(String strKey) {
Expand All @@ -88,11 +95,11 @@ public boolean equals(Object other) {
return false;
}

if (((Key) other).getCount() != getCount()) {
if (((Key) other).count() != count()) {
return false;
}

for (int i = 0; i < getCount(); i++) {
for (int i = 0; i < count(); i++) {
if (!getAt(i).equals(((Key) other).getAt(i))) {
return false;
}
Expand All @@ -104,21 +111,21 @@ public boolean equals(Object other) {
@Override
public int hashCode() {
int hash = 0;
for (int i = 0; i < getCount(); i++) {
for (int i = 0; i < count(); i++) {
hash += getAt(i).hashCode();
}
return hash;
}

public Pair<String, Long> getPrimaryKey() {
public Pair<String, Long> primaryKey() {
return getAt(0);
}

@Override
public String toString() {
StringBuilder str = new StringBuilder("Key(s) ");
for (Pair<String,Long> key : keys.values()) {
str.append(key.key).append("=").append(key.value).append(" ");
str.append(key.getKey()).append("=").append(key.getValue()).append(" ");
}
return str.toString();
}
Expand Down
23 changes: 21 additions & 2 deletions src/main/java/org/viablespark/persistence/Pair.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,33 @@

public class Pair<T, Y> {

public final T key;
public final Y value;
private T key;
private Y value;

public Pair(T key, Y value) {
this.key = key;
this.value = value;
}

public Pair() {
}

public T getKey() {
return key;
}

public void setKey(T key) {
this.key = key;
}

public Y getValue() {
return value;
}

public void setValue(Y value) {
this.value = value;
}

public static <T, Y> Pair<T, Y> of(T key, Y value) {
return new Pair<>(key, value);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/viablespark/persistence/Persistable.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface Persistable {
void setKey(Key key);

default Long getId() {
return getKey().getPrimaryKey().value;
return getKey().primaryKey().getValue();
}

default boolean isNew() {
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/org/viablespark/persistence/dsl/SqlQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,20 @@ public String sql() {
}

Optional<String> whereResult = where.stream()
.map(p -> p.key)
.map(Pair::getKey)
.reduce((acc, a) -> "" + acc + " AND " + a);

String whereStr = whereResult.map(s -> "WHERE " + s).orElse(" ");

Optional<String> condResult = conditions.stream()
.map(p -> p.key)
.map(Pair::getKey)
.reduce((acc, a) -> "" + acc + " " + a);

String condStr = condResult.orElse("");

String orderStr = orderBy != null ? " ORDER BY " +orderBy.key + " " + orderBy.value.toString() : "";
String orderStr = orderBy != null ? " ORDER BY " +orderBy.getKey() + " " + orderBy.getValue().toString() : "";

String limitStr = limit != null ? " LIMIT "+limit.key : "";
String limitStr = limit != null ? " LIMIT "+limit.getKey() : "";

return whereStr + condStr + orderStr + limitStr;
}
Expand All @@ -104,8 +104,8 @@ public Object[] values() {
return rawValues;
}
List<Object> list = new LinkedList<>();
list.addAll(where.stream().map(p -> p.value).collect(Collectors.toList()));
list.addAll(conditions.stream().map(p -> p.value).collect(Collectors.toList()));
list.addAll(where.stream().map(Pair::getValue).collect(Collectors.toList()));
list.addAll(conditions.stream().map(Pair::getValue).collect(Collectors.toList()));
return list.toArray();
}

Expand Down
10 changes: 5 additions & 5 deletions src/main/java/org/viablespark/persistence/dsl/WithSql.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ public static SqlClause getSQLUpdateClause(Persistable entity) throws SQLExcepti
answers.add(deriveValue(m, entity));
}
sql.deleteCharAt(sql.length() - 1); // remove last comma.
Pair<String, Long> primaryKey = entity.getKey().getPrimaryKey();
sql.append(" WHERE ").append(primaryKey.key).append("=?");
answers.add(primaryKey.value);
Pair<String, Long> primaryKey = entity.getKey().primaryKey();
sql.append(" WHERE ").append(primaryKey.getKey()).append("=?");
answers.add(primaryKey.getValue());

//sql = new StringBuilder(sql.toString().replaceAll(".$", ""));
return new SqlClause(sql.toString(), answers.toArray());
Expand Down Expand Up @@ -108,7 +108,7 @@ private static String deriveName(Method m, Persistable entity) throws Exception
Optional<Ref> refOption = getAnnotation(m, entity.getClass(), Ref.class);
if (refOption.isPresent()) {
Persistable refObj = (Persistable) m.invoke(entity);
return refObj.getKey().getPrimaryKey().key;
return refObj.getKey().primaryKey().getKey();
}

String name = m.getName().substring(3);
Expand All @@ -120,7 +120,7 @@ private static Object deriveValue(Method m, Persistable entity) throws Exception
Optional<Ref> refOption = getAnnotation(m, entity.getClass(), Ref.class);
if (refOption.isPresent()) {
Persistable refObj = (Persistable) m.invoke(entity);
return refObj.getKey().getPrimaryKey().value;
return refObj.getKey().primaryKey().getValue();
}
return m.invoke(entity);
}
Expand Down
36 changes: 24 additions & 12 deletions src/test/java/org/viablespark/persistence/KeyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@
package org.viablespark.persistence;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.viablespark.persistence.Key;
import org.viablespark.persistence.Pair;

import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.util.AssertionErrors.assertTrue;
Expand All @@ -43,7 +42,6 @@ public void setupKey(){
key = Key.of("primary", 123L);
key.add("second", 345L);
key.add("third", 567L);

}

@Test
Expand All @@ -52,13 +50,27 @@ public void testConstructorWithMap(){
map.put("primary",123L);
map.put("second",345L);
Key key = new Key(map);
assertEquals(123L,key.getPrimaryKey().value);
assertEquals(123L,key.primaryKey().getValue());
assertEquals(123L,key.getKey("primary"));
}

@Test
public void testSetKeys(){
List<Pair<String,Long>> pairs = new ArrayList<>();
pairs.add(Pair.of("anotherKey",2333L));
Pair<String,Long> p = new Pair<>();
p.setKey("yetAgain");
p.setValue(1243L);
pairs.add(p);
key.setKeys(pairs);

assertEquals(Pair.of("anotherKey",2333L).getValue(), key.getKey("anotherKey") );
assertEquals(p, key.getAt(4) );
}
@Test
public void testOf() {
assertEquals(key.getPrimaryKey(), Key.of("primary", 123L).getPrimaryKey());
assertEquals(key.primaryKey(), Key.of("primary", 123L).primaryKey());
assertEquals(key.primaryKey(), new Key().add("primary",123L).primaryKey() );
}

@Test
Expand All @@ -73,7 +85,7 @@ public void testAdd_String_Number() {

@Test
public void testGetCount() {
assertTrue("Should count as 3", key.getCount() ==3);
assertTrue("Should count as 3", key.count() ==3);
}


Expand All @@ -96,10 +108,10 @@ public void testGetAt() {
key.add("third", 567L);
key.add("primary", 234L);

assertEquals(key.getPrimaryKey(), key.getAt(0));
assertEquals("third", key.getAt(2).key);
assertEquals( "second", key.getAt(1).key);
assertEquals(3, key.getCount());
assertEquals(key.primaryKey(), key.getAt(0));
assertEquals("third", key.getAt(2).getKey());
assertEquals( "second", key.getAt(1).getKey());
assertEquals(3, key.count());

assertNull(key.getAt(23));
}
Expand Down Expand Up @@ -140,7 +152,7 @@ public void testEquals() {

@Test
public void testGetPrimaryKey() {
assertEquals(Pair.of("primary", 123L),key.getPrimaryKey());
assertEquals(Pair.of("primary", 123L),key.primaryKey());
}

@Test
Expand Down
Loading

0 comments on commit 10a74c4

Please sign in to comment.