mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Start PreparedStatement binding documentation, fix some omissions/inconsistencies between QueryMapper and JdbcMapper
This commit is contained in:
parent
73729f5622
commit
29ec477334
@ -25,12 +25,12 @@ public class PreparedStatementUtil {
|
||||
|
||||
public static void setObject(final PreparedStatement ps, final int index, final Object o) throws SQLException {
|
||||
// we are going to put most common ones up top so it should execute faster normally
|
||||
if (o == null || o instanceof String || o instanceof Number)
|
||||
if (o == null || o instanceof String || o instanceof Number || o instanceof Boolean)
|
||||
ps.setObject(index, o);
|
||||
// java.util.Date support, put it in a Timestamp
|
||||
else if (o instanceof java.util.Date)
|
||||
ps.setObject(index, o.getClass().equals(java.util.Date.class) ? new java.sql.Timestamp(((java.util.Date)o).getTime()) : o);
|
||||
//IFJAVA8_START// todo: other java.time types
|
||||
//IFJAVA8_START// todo: other java.time types, Year, ZoneId, ZoneOffset
|
||||
else if (o instanceof Instant)
|
||||
ps.setObject(index, java.sql.Timestamp.from((Instant)o));
|
||||
else if (o instanceof LocalDateTime)
|
||||
@ -73,6 +73,8 @@ public class PreparedStatementUtil {
|
||||
ps.setArray(index, (java.sql.Array) o);
|
||||
else if (o instanceof Enum)
|
||||
ps.setObject(index, ((Enum)o).name());
|
||||
else if (o instanceof java.sql.Ref)
|
||||
ps.setRef(index, (java.sql.Ref) o);
|
||||
else
|
||||
ps.setObject(index, o); // probably won't get here ever, but just in case...
|
||||
/*
|
||||
|
@ -65,8 +65,8 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
return messager;
|
||||
}
|
||||
|
||||
static TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, connectionType, jdbcMapperType,
|
||||
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, iterableType, bindableType, calendarType, cleanerType, enumType;
|
||||
static TypeMirror sqlExceptionType, stringType, numberType, booleanType, utilDateType, readerType, clobType, connectionType, jdbcMapperType,
|
||||
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, refType, collectionType, iterableType, bindableType, calendarType, cleanerType, enumType;
|
||||
//IFJAVA8_START
|
||||
static TypeMirror streamType, instantType, localDateTimeType, localDateType, localTimeType, zonedDateTimeType, offsetDateTimeType, offsetTimeType;
|
||||
//IFJAVA8_END
|
||||
@ -99,6 +99,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
sqlExceptionType = elements.getTypeElement(SQLException.class.getCanonicalName()).asType();
|
||||
stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
|
||||
numberType = elements.getTypeElement(Number.class.getCanonicalName()).asType();
|
||||
booleanType = elements.getTypeElement(Boolean.class.getCanonicalName()).asType();
|
||||
utilDateType = elements.getTypeElement(java.util.Date.class.getCanonicalName()).asType();
|
||||
readerType = elements.getTypeElement(Reader.class.getCanonicalName()).asType();
|
||||
clobType = elements.getTypeElement(Clob.class.getCanonicalName()).asType();
|
||||
@ -124,6 +125,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
//byteArrayType = elements.getTypeElement(byte.class.getCanonicalName()).asType();
|
||||
byteArrayType = types.getArrayType(types.getPrimitiveType(TypeKind.BYTE));
|
||||
sqlArrayType = elements.getTypeElement(java.sql.Array.class.getCanonicalName()).asType();
|
||||
refType = elements.getTypeElement(java.sql.Ref.class.getCanonicalName()).asType();
|
||||
collectionType = types.getDeclaredType(elements.getTypeElement(Collection.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||
iterableType = types.getDeclaredType(elements.getTypeElement(Iterable.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||
|
||||
@ -987,7 +989,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
// we are going to put most common ones up top so it should execute faster normally
|
||||
// todo: avoid string concat here
|
||||
if (method == null)
|
||||
if (o.getKind().isPrimitive() || types.isAssignable(o, stringType) || types.isAssignable(o, numberType)) {
|
||||
if (o.getKind().isPrimitive() || types.isAssignable(o, stringType) || types.isAssignable(o, numberType) || types.isAssignable(o, booleanType)) {
|
||||
method = "Object";
|
||||
// java.util.Date support, put it in a Timestamp
|
||||
} else if (types.isAssignable(o, utilDateType)) {
|
||||
@ -1038,6 +1040,8 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
} else if (types.isAssignable(o, enumType)) {
|
||||
method = "Object";
|
||||
variableName = variableName + " == null ? null : " + variableName + ".name()";
|
||||
} else if (types.isAssignable(o, refType)) {
|
||||
method = "Ref";
|
||||
} else {
|
||||
// shouldn't get here ever, if we do the types should be more specific
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@JdbcMapper.SQL could not properly infer PreparedStatement bind call for param", param);
|
||||
|
139
readme.md
139
readme.md
@ -429,6 +429,143 @@ String s = rs.getString(index);
|
||||
return s == null ? null : ZoneOffset.of(s);
|
||||
```
|
||||
|
||||
Object to Column (PreparedStatement) Mapping
|
||||
------------------------
|
||||
|
||||
This explains how specific java types map to specific PreparedStatement calls, this can be different between JdbcMapper and QueryMapper because of the
|
||||
different information available. With JdbcMapper we have type information regardless of the value, so a String is a String even if you send in null. With
|
||||
QueryMapper if the value is null, we have no idea if that was supposed to be a Date or a String or what.
|
||||
|
||||
If you are thinking 'shut up and show me the code already' refer to [PreparedStatementUtil.java](https://github.com/moparisthebest/JdbcMapper/blob/master/common/src/main/java/com/moparisthebest/jdbc/util/PreparedStatementUtil.java#L26) for the runtime mapping, and [JdbcMapperProcessor.java](https://github.com/moparisthebest/JdbcMapper/blob/master/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java#L918) for the compile-time mapping, which should end up being identical where possible.
|
||||
|
||||
For the purposes of this mapping, consider 'ps' an instance of PreparedStatement, 'index' an int index of a PreparedStatement column, and 'o' as the Object being mapped to the PreparedStatement column.
|
||||
|
||||
### Misc Objects
|
||||
##### String / Number / Boolean / primitives
|
||||
```java
|
||||
ps.setObject(index, o);
|
||||
```
|
||||
##### null
|
||||
This only applies at runtime, in which case we don't have a type, we always have a type at compile-time.
|
||||
```java
|
||||
ps.setObject(index, o);
|
||||
```
|
||||
##### java.lang.Enum (any enum)
|
||||
```java
|
||||
ps.setObject(index, o.name());
|
||||
```
|
||||
##### byte[]
|
||||
```java
|
||||
ps.setBlob(index, new ByteArrayInputStream(o));
|
||||
```
|
||||
##### java.sql.Ref
|
||||
```java
|
||||
ps.setRef(index, o);
|
||||
```
|
||||
##### java.sql.Blob / java.io.InputStream
|
||||
```java
|
||||
ps.setBlob(index, o);
|
||||
```
|
||||
##### String as Blob
|
||||
Where `s` is the String, and `charset` is the character set to convert the String to bytes with,
|
||||
if not provided, charset defaults to UTF-8:
|
||||
```java
|
||||
ps.setBlob(index, s == null ? null : new ByteArrayInputStream(s.getBytes(charset)));
|
||||
```
|
||||
At runtime using QueryMapper, you signal you want this by wrapping s with `PreparedStatementUtil.wrapBlob(s)` or `PreparedStatementUtil.wrapBlob(s, charset)`
|
||||
At compile-time using JdbcMapper, you signal you want this in the SQL like `{blob:s}` or `{blob:utf-8:s}` any charset supported by your java works
|
||||
##### java.io.File
|
||||
```java
|
||||
try {
|
||||
ps.setBlob(index, new FileInputStream(o)); // todo: does this close this or leak a file descriptor?
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new SQLException("File to Blob FileNotFoundException", e);
|
||||
}
|
||||
```
|
||||
This will likely change in the near future to read file to byte[] and behave like byte[] from above, since we probably
|
||||
can't count on the FileInputStream being properly closed...
|
||||
##### java.sql.Clob / java.io.Reader
|
||||
```java
|
||||
ps.setClob(index, o);
|
||||
```
|
||||
##### String as Clob
|
||||
Where `s` is the String:
|
||||
```java
|
||||
ps.setClob(index, s == null ? null : new StringReader(s));
|
||||
```
|
||||
At runtime using QueryMapper, you signal you want this by wrapping s with `PreparedStatementUtil.wrapClob(s)`
|
||||
At compile-time using JdbcMapper, you signal you want this in the SQL like `{clob:s}`
|
||||
##### java.sql.Array
|
||||
```java
|
||||
ps.setRef(index, o);
|
||||
```
|
||||
##### *
|
||||
If nothing else fits, we call setObject and cross our fingers with QueryMapper at runtime, this is a compile-time error
|
||||
with JdbcMapper.
|
||||
```java
|
||||
ps.setObject(index, o);
|
||||
```
|
||||
### Date/Time Objects
|
||||
##### exactly java.util.Date
|
||||
```java
|
||||
ps.setObject(index, new java.sql.Timestamp(o.getTime());
|
||||
```
|
||||
##### instanceof java.util.Date, but not exactly java.util.Date
|
||||
so from stdlib this includes java.sql.Date, java.sql.Timestamp, and java.sql.Time
|
||||
```java
|
||||
ps.setObject(index, o);
|
||||
```
|
||||
##### java.time.Instant
|
||||
```java
|
||||
ps.setObject(index, java.sql.Timestamp.from(o);
|
||||
```
|
||||
##### java.time.LocalDateTime
|
||||
```java
|
||||
ps.setObject(index, java.sql.Timestamp.valueOf(o));
|
||||
```
|
||||
##### java.time.LocalDate
|
||||
```java
|
||||
ps.setObject(index, java.sql.Date.valueOf(o));
|
||||
```
|
||||
##### java.time.LocalTime
|
||||
```java
|
||||
ps.setObject(index, java.sql.Time.valueOf(o));
|
||||
```
|
||||
##### java.time.ZonedDateTime
|
||||
```java
|
||||
ps.setObject(index, java.sql.Timestamp.from(o.toInstant()));
|
||||
```
|
||||
##### java.time.OffsetDateTime
|
||||
```java
|
||||
ps.setObject(index, java.sql.Timestamp.from(o.toInstant()));
|
||||
```
|
||||
##### java.time.OffsetTime
|
||||
```java
|
||||
ps.setObject(index, java.sql.Time.valueOf(o.toLocalTime()));
|
||||
```
|
||||
##### java.time.Year
|
||||
done this way instead of Year.of(int) because usually int->string database coercion is allowed and the other way is not
|
||||
```java
|
||||
// todo
|
||||
```
|
||||
##### java.time.ZoneId
|
||||
```java
|
||||
// todo
|
||||
```
|
||||
##### java.time.ZoneOffset
|
||||
```java
|
||||
// todo
|
||||
```
|
||||
### Special objects
|
||||
##### InLists
|
||||
```java
|
||||
// todo
|
||||
```
|
||||
##### Bindable / SqlBuilder
|
||||
```java
|
||||
// todo
|
||||
```
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
@ -439,4 +576,4 @@ TODO
|
||||
* CompilingResultSetMapper fails on inner class like 'public static class Bla {'
|
||||
* Support Optional<T> for all T instead of null
|
||||
* change boolean to be consistent with other primitives?
|
||||
* make sure 'fallback to resultSet.toObject()' never happens at compile-time with JdbcMapper
|
||||
* make sure 'fallback to resultSet.toObject()' never happens at compile-time with JdbcMapper
|
||||
|
Loading…
Reference in New Issue
Block a user