Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.itranswarp.warpdb.AccessibleProperty Maven / Gradle / Ivy
package com.itranswarp.warpdb;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import com.itranswarp.warpdb.converter.EnumToStringConverter;
import com.itranswarp.warpdb.util.ClassUtils;
/**
* Represent a bean property.
*
* @author liaoxuefeng
*/
class AccessibleProperty {
// Field or Method:
final AccessibleObject accessible;
// java type:
final Class> propertyType;
// converter:
final AttributeConverter converter;
// java bean property name:
final String propertyName;
// table column name:
final String columnName;
// column DDL:
final String columnDefinition;
// getter and do convert if necessary:
final PropertyGetter convertGetter;
// setter and do convert if necessary:
final PropertySetter convertSetter;
final boolean nullable;
final boolean unique;
boolean isId() {
return this.accessible.isAnnotationPresent(Id.class);
}
// is id && is id marked as @GeneratedValue(strategy=GenerationType.IDENTITY)
boolean isIdentityId() {
if (!isId()) {
return false;
}
GeneratedValue gv = this.accessible.getAnnotation(GeneratedValue.class);
if (gv == null) {
return false;
}
GenerationType gt = gv.strategy();
return gt == GenerationType.IDENTITY;
}
boolean isVersion() {
boolean isVersion = this.accessible.isAnnotationPresent(Version.class);
if (isVersion) {
if (!VERSION_TYPES.contains(this.propertyType)) {
throw new RuntimeException("Unsupported @Version type: " + this.propertyType.getName());
}
}
return isVersion;
}
boolean isInsertable() {
if (isIdentityId()) {
return false;
}
Column col = this.accessible.getAnnotation(Column.class);
return col == null || col.insertable();
}
boolean isUpdatable() {
if (isId()) {
return false;
}
Column col = this.accessible.getAnnotation(Column.class);
return col == null || col.updatable();
}
static PropertySetter createPropertySetter(Field f) {
if (f.getType() == Long.class || f.getType() == long.class) {
return (obj, value) -> {
if (value instanceof BigInteger) {
value = ((BigInteger) value).longValue();
}
f.set(obj, value);
};
}
return (obj, value) -> {
f.set(obj, value);
};
}
static PropertySetter createPropertySetter(Method setter) {
if (setter.getParameterTypes()[0] == Long.class || setter.getParameterTypes()[0] == long.class) {
return (obj, value) -> {
if (value instanceof BigInteger) {
value = ((BigInteger) value).longValue();
}
setter.invoke(obj, value);
};
}
return (obj, value) -> {
setter.invoke(obj, value);
};
}
public AccessibleProperty(Field f) {
this(f.getType(), f.getName(), f, (obj) -> {
return f.get(obj);
}, createPropertySetter(f));
}
public AccessibleProperty(String name, Method getter, Method setter) {
this(getter.getReturnType(), name, getter, (obj) -> {
return getter.invoke(obj);
}, createPropertySetter(setter));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private AccessibleProperty(final Class> type, final String propertyName, final AccessibleObject accessible, final PropertyGetter getter,
final PropertySetter setter) {
accessible.setAccessible(true);
this.accessible = accessible;
// check:
AttributeConverter converter = getConverter(accessible);
String columnDefinition = null;
if (converter == null && type.isEnum()) {
converter = new EnumToStringConverter(type);
columnDefinition = "VARCHAR(50)";
}
final Class> propertyType = checkPropertyType(type, converter);
final String columnName = getColumnName(accessible, propertyName);
if (columnDefinition == null) {
Class> ddlType = getConverterType(converter);
if (ddlType == null) {
ddlType = propertyType;
}
columnDefinition = getColumnDefinition(accessible, ddlType);
} // init:
this.nullable = isNullable();
this.unique = isUnique();
this.converter = converter;
this.propertyType = propertyType;
this.propertyName = propertyName;
this.columnName = columnName;
this.columnDefinition = columnDefinition;
this.convertGetter = this.converter == null ? getter : (bean) -> {
Object value = getter.get(bean);
if (value != null) {
value = this.converter.convertToDatabaseColumn(value);
}
return value;
};
this.convertSetter = this.converter == null ? setter : (bean, value) -> {
if (value != null && this.converter != null) {
value = this.converter.convertToEntityAttribute(value);
}
setter.set(bean, value);
};
}
private boolean isNullable() {
if (isId()) {
return false;
}
Column col = this.accessible.getAnnotation(Column.class);
return col == null || col.nullable();
}
private boolean isUnique() {
if (isId()) {
return true;
}
Column col = this.accessible.getAnnotation(Column.class);
return col != null && col.unique();
}
@SuppressWarnings("unchecked")
private AttributeConverter getConverter(AccessibleObject accessible) {
Convert converter = accessible.getAnnotation(Convert.class);
if (converter != null) {
Class> converterClass = converter.converter();
if (!AttributeConverter.class.isAssignableFrom(converterClass)) {
throw new RuntimeException("Converter class must be AttributeConverter rather than " + converterClass.getName());
}
try {
Constructor> cs = converterClass.getDeclaredConstructor();
cs.setAccessible(true);
return (AttributeConverter) cs.newInstance();
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | InvocationTargetException e) {
throw new RuntimeException("Cannot instantiate Converter: " + converterClass.getName(), e);
}
}
return null;
}
private static Class> getConverterType(AttributeConverter converter) {
if (converter != null) {
List types = ClassUtils.getGenericInterfacesIncludeHierarchy(converter.getClass());
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
if (pt.getRawType() == AttributeConverter.class) {
Type dbType = pt.getActualTypeArguments()[1];
if (dbType instanceof Class) {
return (Class>) dbType;
}
}
}
}
}
return null;
}
private static Class> checkPropertyType(Class> typeClass, AttributeConverter converter) {
Class> converterType = getConverterType(converter);
if (converterType != null) {
typeClass = converterType;
}
if (typeClass.isEnum() || DEFAULT_COLUMN_TYPES.containsKey(typeClass)) {
return typeClass;
}
throw new RuntimeException("Unsupported type: " + typeClass);
}
private static String getColumnName(AccessibleObject ao, String defaultName) {
Column col = ao.getAnnotation(Column.class);
if (col == null || col.name().isEmpty()) {
return defaultName;
}
return col.name();
}
private static String getColumnDefinition(AccessibleObject ao, Class> type) {
Column col = ao.getAnnotation(Column.class);
String colDef = null;
if (col == null || col.columnDefinition().isEmpty()) {
if (type.isEnum()) {
colDef = "VARCHAR(50)";
} else {
colDef = getDefaultColumnType(type, col);
}
} else {
colDef = col.columnDefinition().toUpperCase();
}
return colDef;
}
private static String getDefaultColumnType(Class> type, Column col) {
String ddl = DEFAULT_COLUMN_TYPES.get(type);
if (ddl.equals("VARCHAR($1)")) {
ddl = ddl.replace("$1", String.valueOf(col == null ? 255 : col.length()));
}
if (ddl.equals("DECIMAL($1,$2)")) {
int preci = col == null ? 0 : col.precision();
int scale = col == null ? 0 : col.scale();
if (preci == 0) {
preci = 10; // default DECIMAL precision of MySQL
}
ddl = ddl.replace("$1", String.valueOf(preci)).replace("$2", String.valueOf(scale));
}
return ddl;
}
static final Map, String> DEFAULT_COLUMN_TYPES = new HashMap<>();
static final Set> VERSION_TYPES = new HashSet<>();
static {
DEFAULT_COLUMN_TYPES.put(String.class, "VARCHAR($1)");
DEFAULT_COLUMN_TYPES.put(boolean.class, "BIT");
DEFAULT_COLUMN_TYPES.put(Boolean.class, "BIT");
DEFAULT_COLUMN_TYPES.put(byte.class, "TINYINT");
DEFAULT_COLUMN_TYPES.put(Byte.class, "TINYINT");
DEFAULT_COLUMN_TYPES.put(short.class, "SMALLINT");
DEFAULT_COLUMN_TYPES.put(Short.class, "SMALLINT");
DEFAULT_COLUMN_TYPES.put(int.class, "INTEGER");
DEFAULT_COLUMN_TYPES.put(Integer.class, "INTEGER");
DEFAULT_COLUMN_TYPES.put(long.class, "BIGINT");
DEFAULT_COLUMN_TYPES.put(Long.class, "BIGINT");
DEFAULT_COLUMN_TYPES.put(float.class, "REAL");
DEFAULT_COLUMN_TYPES.put(Float.class, "REAL");
DEFAULT_COLUMN_TYPES.put(double.class, "DOUBLE");
DEFAULT_COLUMN_TYPES.put(Double.class, "DOUBLE");
DEFAULT_COLUMN_TYPES.put(BigDecimal.class, "DECIMAL($1,$2)");
DEFAULT_COLUMN_TYPES.put(java.sql.Date.class, "DATE");
DEFAULT_COLUMN_TYPES.put(LocalDate.class, "DATE");
DEFAULT_COLUMN_TYPES.put(LocalTime.class, "TIME");
DEFAULT_COLUMN_TYPES.put(java.util.Date.class, "DATETIME");
DEFAULT_COLUMN_TYPES.put(java.sql.Timestamp.class, "TIMESTAMP");
DEFAULT_COLUMN_TYPES.put(java.sql.Blob.class, "BLOB");
DEFAULT_COLUMN_TYPES.put(java.sql.Clob.class, "CLOB");
VERSION_TYPES.addAll(Arrays.asList(long.class, Long.class, int.class, Integer.class, short.class, Short.class, java.sql.Timestamp.class));
}
}