mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Rearrange readme
This commit is contained in:
parent
e896a66e0e
commit
d85e5ce7f1
230
readme.md
230
readme.md
@ -24,6 +24,121 @@ Goals
|
|||||||
4. Be runnable and testable inside or outside of containers easily
|
4. Be runnable and testable inside or outside of containers easily
|
||||||
5. No surprises, as little magic as possible
|
5. No surprises, as little magic as possible
|
||||||
|
|
||||||
|
JdbcMapper
|
||||||
|
----------
|
||||||
|
|
||||||
|
Write an interface or abstract class with methods that make sense for accessing your database, annotate the methods with
|
||||||
|
SQL, and on compilation an annotation processor will generate the required java.sql API code to execute your query and
|
||||||
|
return what you wanted. This code is guaranteed to be the fastest code possible because hand written code would look
|
||||||
|
the same, just more error prone and harder to maintain. The annotation processor also checks that the SQL queries are
|
||||||
|
valid, have all the right bind parameters, and can bind the result columns to all the correct fields on the result object.
|
||||||
|
If anything is wrong it's a compile error pointing you to the exact problem.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@JdbcMapper.Mapper(jndiName = "java:/comp/env/jdbc/testPool") // omit jndiName and you must send in a java.sql.Connection
|
||||||
|
public interface PersonDAO extends Closeable { // Closeable is optional but must have a 'void close()' method to use cachePreparedStatements or jndiName
|
||||||
|
|
||||||
|
@JdbcMapper.SQL("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)")
|
||||||
|
void createTablePerson();
|
||||||
|
|
||||||
|
@JdbcMapper.SQL("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES ({personNo}, {birthDate}, {firstName}, {lastName})")
|
||||||
|
int insertPerson(long personNo, Date birthDate, String firstName, String lastName);
|
||||||
|
|
||||||
|
@JdbcMapper.SQL("UPDATE person SET first_name = {firstName} WHERE person_no = {personNo}")
|
||||||
|
int setFirstName(String firstName, long personNo); // returning int will return number of rows modified, can also return void
|
||||||
|
|
||||||
|
@JdbcMapper.SQL("SELECT first_name FROM person WHERE person_no = {personNo}")
|
||||||
|
String getFirstName(long personNo) throws SQLException; // can map directly to simple types
|
||||||
|
|
||||||
|
@JdbcMapper.SQL("SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = {personNo}")
|
||||||
|
Person getPerson(long personNo) throws SQLException; // or multiple fields, set methods, or constructor parameters on a POJO
|
||||||
|
|
||||||
|
@JdbcMapper.SQL("SELECT person_no, first_name, last_name, birth_date FROM person WHERE last_name = {lastName}")
|
||||||
|
List<Person> getPeople(String lastName) throws SQLException; // all rows in any Collection<T> (like Set<T>, LinkedList<T> etc), T[], ResultSetIterable<T> or Stream<T> (java8+) works too
|
||||||
|
}
|
||||||
|
|
||||||
|
// code:
|
||||||
|
try(PersonDAO personDao = JdbcMapperFactory.create(PersonDAO.class)) {
|
||||||
|
personDao.createTablePerson();
|
||||||
|
System.out.println(personDao.insertPerson(0, null, "First", "Person")); // 1
|
||||||
|
System.out.println(personDao.insertPerson(1, null, "First", "Person")); // 1
|
||||||
|
System.out.println(personDao.setFirstName("Second", 1)); // 1
|
||||||
|
|
||||||
|
System.out.println(personDao.getFirstName(0)); // First
|
||||||
|
System.out.println(personDao.getFirstName(1)); // Second
|
||||||
|
|
||||||
|
System.out.println(personDao.getPerson(0)); // Person{personNo=0,birthDate=null,firstName=First,lastName=Person}
|
||||||
|
System.out.println(personDao.getPerson(1)); // Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}
|
||||||
|
|
||||||
|
System.out.println(personDao.getPeople("Person")); // [Person{personNo=0,birthDate=null,firstName=First,lastName=Person}, Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
QueryMapper
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Need to generate SQL dynamically or just execute some queries quickly and easily? Mapping is done using reflection in
|
||||||
|
ResultSetMapper or code is dynamically generated, compiled, instantiated, and cached at runtime to do the mapping using
|
||||||
|
CompilingResultSetMapper.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// CompilingResultSetMapper is an alternative to ResultSetMapper, default is ResultSetMapper
|
||||||
|
try(QueryMapper qm = new QueryMapper("java:/comp/env/jdbc/testPool", new ResultSetMapper())) { // or send in java.sql.Connection
|
||||||
|
// executeUpdate returns int
|
||||||
|
qm.executeUpdate("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)");
|
||||||
|
System.out.println(qm.executeUpdate("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES (?, ?, ?, ?)", 0, null, "First", "Person")); // 1
|
||||||
|
System.out.println(qm.executeUpdate("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES (?, ?, ?, ?)", 1, null, "First", "Person")); // 1
|
||||||
|
System.out.println(qm.executeUpdate("UPDATE person SET first_name = ? WHERE person_no = ?", "Second", 1)); // 1
|
||||||
|
|
||||||
|
// can map directly to simple types
|
||||||
|
System.out.println(qm.toObject("SELECT first_name FROM person WHERE person_no = ?", String.class, 0)); // First
|
||||||
|
System.out.println(qm.toObject("SELECT first_name FROM person WHERE person_no = ?", String.class, 1)); // Second
|
||||||
|
|
||||||
|
// or multiple fields, set methods, or constructor parameters on a POJO
|
||||||
|
System.out.println(qm.toObject("SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = ?", String.class, 0)); // Person{personNo=0,birthDate=null,firstName=First,lastName=Person}
|
||||||
|
System.out.println(qm.toObject("SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = ?", String.class, 1)); // Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}
|
||||||
|
|
||||||
|
// instead of toCollection can use toList, toArray, toResultSetIterable, toStream (java8+)
|
||||||
|
System.out.println(qm.toCollection("SELECT person_no, first_name, last_name, birth_date FROM person WHERE last_name = ?", new ArrayList<String>(), String.class, "Person")); // [Person{personNo=0,birthDate=null,firstName=First,lastName=Person}, Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
ResultSet (multiple rows) to Object/Collection Mapping
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
todo: document
|
||||||
|
|
||||||
|
Row to Object Mapping
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
In cases of only one column being returned from the query (or two in the case of Map<K,V>), the same simple
|
||||||
|
column -> Object mapping described below will take place. If a more complex object is requested, column names or
|
||||||
|
indices are used to decide how to construct/map the object.
|
||||||
|
|
||||||
|
A single row can be represented by 3 main Objects:
|
||||||
|
|
||||||
|
1. Array, where each column is mapped by index, starting at 0, array type of course determines the type returned
|
||||||
|
2. Map<String, ?>, where each column is mapped by name as key, and column value as value, mapped according to type
|
||||||
|
* consider using the supplied com.moparisthebest.jdbc.util.CaseInsensitiveHashMap where case is ignored for keys
|
||||||
|
3. Custom class Object, which attempts many different ways to map all returned columns to the class, if one of these
|
||||||
|
is not a perfect match, an exception is thrown at runtime with QueryMapper, and a compile-time error happens with
|
||||||
|
JdbcMapper. This is an ordered list of how rows are mapped to class objects:
|
||||||
|
1. If the class has a public constructor that takes a single java.sql.ResultSet parameter and nothing else, each
|
||||||
|
row is sent in to create a new object, nothing else is done.
|
||||||
|
2. If the class has a public constructor that takes the same number of arguments as columns returned, and all names
|
||||||
|
match (order does not matter), this constructor is used. This method has some requirements though:
|
||||||
|
* Java 8+ only
|
||||||
|
* requires -parameters argument to javac for runtime with QueryMapper, or compiling against classes without
|
||||||
|
source with JdbcMapper
|
||||||
|
* Beware Java 8 only Bug ID [JDK-8191074](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8191074),
|
||||||
|
fixed in Java 9+ but will not be backported to 8
|
||||||
|
|
||||||
|
todo: explain how rows are mapped to POJOs
|
||||||
|
|
||||||
Column to Object Mapping
|
Column to Object Mapping
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
@ -229,121 +344,6 @@ String s = rs.getString(index);
|
|||||||
return s == null ? null : ZoneOffset.of(s);
|
return s == null ? null : ZoneOffset.of(s);
|
||||||
```
|
```
|
||||||
|
|
||||||
Row to Object Mapping
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
In cases of only one column being returned from the query (or two in the case of Map<K,V>), the same simple
|
|
||||||
column -> Object mapping described above will take place. If a more complex object is requested, column names or
|
|
||||||
indices are used to decide how to construct/map the object.
|
|
||||||
|
|
||||||
A single row can be represented by 3 main Objects:
|
|
||||||
|
|
||||||
1. Array, where each column is mapped by index, starting at 0, array type of course determines the type returned
|
|
||||||
2. Map<String, ?>, where each column is mapped by name as key, and column value as value, mapped according to type
|
|
||||||
* consider using the supplied com.moparisthebest.jdbc.util.CaseInsensitiveHashMap where case is ignored for keys
|
|
||||||
3. Custom class Object, which attempts many different ways to map all returned columns to the class, if one of these
|
|
||||||
is not a perfect match, an exception is thrown at runtime with QueryMapper, and a compile-time error happens with
|
|
||||||
JdbcMapper. This is an ordered list of how rows are mapped to class objects:
|
|
||||||
1. If the class has a public constructor that takes a single java.sql.ResultSet parameter and nothing else, each
|
|
||||||
row is sent in to create a new object, nothing else is done.
|
|
||||||
2. If the class has a public constructor that takes the same number of arguments as columns returned, and all names
|
|
||||||
match (order does not matter), this constructor is used. This method has some requirements though:
|
|
||||||
* Java 8+ only
|
|
||||||
* requires -parameters argument to javac for runtime with QueryMapper, or compiling against classes without
|
|
||||||
source with JdbcMapper
|
|
||||||
* Beware Java 8 only Bug ID [JDK-8191074](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8191074),
|
|
||||||
fixed in Java 9+ but will not be backported to 8
|
|
||||||
|
|
||||||
todo: explain how rows are mapped to POJOs
|
|
||||||
|
|
||||||
ResultSet (multiple rows) to Object/Collection Mapping
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
todo: document
|
|
||||||
|
|
||||||
JdbcMapper
|
|
||||||
----------
|
|
||||||
|
|
||||||
Write an interface or abstract class with methods that make sense for accessing your database, annotate the methods with
|
|
||||||
SQL, and on compilation an annotation processor will generate the required java.sql API code to execute your query and
|
|
||||||
return what you wanted. This code is guaranteed to be the fastest code possible because hand written code would look
|
|
||||||
the same, just more error prone and harder to maintain. The annotation processor also checks that the SQL queries are
|
|
||||||
valid, have all the right bind parameters, and can bind the result columns to all the correct fields on the result object.
|
|
||||||
If anything is wrong it's a compile error pointing you to the exact problem.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
@JdbcMapper.Mapper(jndiName = "java:/comp/env/jdbc/testPool") // omit jndiName and you must send in a java.sql.Connection
|
|
||||||
public interface PersonDAO extends Closeable { // Closeable is optional but must have a 'void close()' method to use cachePreparedStatements or jndiName
|
|
||||||
|
|
||||||
@JdbcMapper.SQL("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)")
|
|
||||||
void createTablePerson();
|
|
||||||
|
|
||||||
@JdbcMapper.SQL("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES ({personNo}, {birthDate}, {firstName}, {lastName})")
|
|
||||||
int insertPerson(long personNo, Date birthDate, String firstName, String lastName);
|
|
||||||
|
|
||||||
@JdbcMapper.SQL("UPDATE person SET first_name = {firstName} WHERE person_no = {personNo}")
|
|
||||||
int setFirstName(String firstName, long personNo); // returning int will return number of rows modified, can also return void
|
|
||||||
|
|
||||||
@JdbcMapper.SQL("SELECT first_name FROM person WHERE person_no = {personNo}")
|
|
||||||
String getFirstName(long personNo) throws SQLException; // can map directly to simple types
|
|
||||||
|
|
||||||
@JdbcMapper.SQL("SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = {personNo}")
|
|
||||||
Person getPerson(long personNo) throws SQLException; // or multiple fields, set methods, or constructor parameters on a POJO
|
|
||||||
|
|
||||||
@JdbcMapper.SQL("SELECT person_no, first_name, last_name, birth_date FROM person WHERE last_name = {lastName}")
|
|
||||||
List<Person> getPeople(String lastName) throws SQLException; // all rows in any Collection<T> (like Set<T>, LinkedList<T> etc), T[], ResultSetIterable<T> or Stream<T> (java8+) works too
|
|
||||||
}
|
|
||||||
|
|
||||||
// code:
|
|
||||||
try(PersonDAO personDao = JdbcMapperFactory.create(PersonDAO.class)) {
|
|
||||||
personDao.createTablePerson();
|
|
||||||
System.out.println(personDao.insertPerson(0, null, "First", "Person")); // 1
|
|
||||||
System.out.println(personDao.insertPerson(1, null, "First", "Person")); // 1
|
|
||||||
System.out.println(personDao.setFirstName("Second", 1)); // 1
|
|
||||||
|
|
||||||
System.out.println(personDao.getFirstName(0)); // First
|
|
||||||
System.out.println(personDao.getFirstName(1)); // Second
|
|
||||||
|
|
||||||
System.out.println(personDao.getPerson(0)); // Person{personNo=0,birthDate=null,firstName=First,lastName=Person}
|
|
||||||
System.out.println(personDao.getPerson(1)); // Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}
|
|
||||||
|
|
||||||
System.out.println(personDao.getPeople("Person")); // [Person{personNo=0,birthDate=null,firstName=First,lastName=Person}, Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
QueryMapper
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Need to generate SQL dynamically or just execute some queries quickly and easily? Mapping is done using reflection in
|
|
||||||
ResultSetMapper or code is dynamically generated, compiled, instantiated, and cached at runtime to do the mapping using
|
|
||||||
CompilingResultSetMapper.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
// CompilingResultSetMapper is an alternative to ResultSetMapper, default is ResultSetMapper
|
|
||||||
try(QueryMapper qm = new QueryMapper("java:/comp/env/jdbc/testPool", new ResultSetMapper())) { // or send in java.sql.Connection
|
|
||||||
// executeUpdate returns int
|
|
||||||
qm.executeUpdate("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)");
|
|
||||||
System.out.println(qm.executeUpdate("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES (?, ?, ?, ?)", 0, null, "First", "Person")); // 1
|
|
||||||
System.out.println(qm.executeUpdate("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES (?, ?, ?, ?)", 1, null, "First", "Person")); // 1
|
|
||||||
System.out.println(qm.executeUpdate("UPDATE person SET first_name = ? WHERE person_no = ?", "Second", 1)); // 1
|
|
||||||
|
|
||||||
// can map directly to simple types
|
|
||||||
System.out.println(qm.toObject("SELECT first_name FROM person WHERE person_no = ?", String.class, 0)); // First
|
|
||||||
System.out.println(qm.toObject("SELECT first_name FROM person WHERE person_no = ?", String.class, 1)); // Second
|
|
||||||
|
|
||||||
// or multiple fields, set methods, or constructor parameters on a POJO
|
|
||||||
System.out.println(qm.toObject("SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = ?", String.class, 0)); // Person{personNo=0,birthDate=null,firstName=First,lastName=Person}
|
|
||||||
System.out.println(qm.toObject("SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = ?", String.class, 1)); // Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}
|
|
||||||
|
|
||||||
// instead of toCollection can use toList, toArray, toResultSetIterable, toStream (java8+)
|
|
||||||
System.out.println(qm.toCollection("SELECT person_no, first_name, last_name, birth_date FROM person WHERE last_name = ?", new ArrayList<String>(), String.class, "Person")); // [Person{personNo=0,birthDate=null,firstName=First,lastName=Person}, Person{personNo=1,birthDate=null,firstName=Second,lastName=Person}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user