La diferencia entre # y $ en Mybatis y su implementación
La mayoría de nosotros usamos # {} porque puede evitar la inyección de SQL, pero a veces $ {} aún es necesario, como pasar nombre de tabla o nombre de campo como Ordenar por id||hora, esto necesitas $ {} para pasar #{}
TypeHandler no puede funcionar en ${}
Solo comprendiendo cómo funcionan las herramientas podrás usarlas mejor.
Primero introduzca una clase que analice # {} y $ {}, donde $ {} se reemplaza por un par de valores correspondientes y # {} se reemplaza por? Y aquí se analizan los campos de este atributo, incluido el tipo de juicio, etc.
Clase pública GenericTokenParser {
Cadena final privada openToken//Esto es como # {o $ {
Cadena final privada closeToken//Esto es básicamente Sí}
Manejador TokenHandler final privado; //Obtiene el valor de la clave según #{key} o ${key}
public GenericTokenParser (String open token, String closeToken, Programa de procesamiento TokenHandler). ) {
este. token abierto = token abierto;
this.closeToken = closeToken
this.handler = manejador
}
/**
*Esto es para reemplazar ${key}, luego obtener el valor a través de hanlder y unirlo.
**/
Análisis de cadenas públicas (texto de cadena) {
Constructor StringBuilder = new StringBuilder();
If (texto != nulo & amp& amptext . length()& gt; 0) {
char[]src = tochararray();
int offset = 0;
char[]src = text . tochararray();
int offset = 0;
p>int start = text índice de (token abierto, offset);
while(start>-1) {
if(start>0& amp& ampsrc[start - 1] == '\\') {
//Variables están escapados. Elimine las barras invertidas.
builder.append(fuente, desplazamiento, inicio - 1).
append(token de apertura);
desplazamiento = inicio+token de apertura();
}else {
int end = text.indexOf(closeToken, inicio);
if (end == -1) {
builder.append(src, offset, src . length-offset); src.length
} En caso contrario {
builder.append(src, offset, start-offset);
offset = inicio+token abierto(). ;
Contenido de cadena = nueva cadena (src, offset, end-offset); //Obtener la clave en #{key}||${key}
builder . handler .handle token(content)); //Obtiene el "valor" correspondiente según la clave.
desplazamiento = fin+closetoken . longitud();
}
}
inicio = text.indexOf(openToken, desplazamiento) ;
}
if(desplazamiento & lt; longitud){
builder.append(src, desplazamiento, src . longitud-desplazamiento);
}
}
Devuelve builder.tostring();
}
}
De hecho, la mayor diferencia es la clase de implementación de análisis de las siguientes dos clases de implementación diferentes
1. ${}
Determina el tipo de parámetro, luego fija el valor, sin agregar nada más, el Ognl universal.
La clase estática privada BindingTokenParser implementa TokenHandler {
Contexto privado DynamicContext
BindingTokenParser público (contexto de contexto dinámico) {
this.context; = contexto
}
Cadena pública handleToken(contenido de cadena){
parámetro de objeto = contexto obtener enlaces(). get(" _ parámetro ");
if (parámetro == null) {
context.getBindings(). put("valor ",null);
} else if(registro de tipo simple. issimpletype(parámetro. getclass())){
context.getBindings().
put("valor", parámetro);
}
valor del objeto = ognlcache . getvalue(contenido, contexto. obtener enlaces()); valor == nulo? " ":cadena valor de(valor)); // El número 274 devuelve "" en lugar de " nulo "
}
}
2.# {} clase de implementación de análisis
Esto es más complicado, a juzgar por javaType, typeHandler y precisión digital. A través de Handler, podemos procesar algunos datos de columnas complejos.
El parámetro de clase estática privada MappingTokenHandler extiende BaseBuilder para implementar TokenHandler {
Lista de distribución personal & ltparameter mapeo& gtparameter mapeos = new ArrayList & lt;parameter mapeo>();
Clase privada<? & gtparameterType
Metaparámetro de metaobjeto privado;
parámetro público MappingTokenHandler (configuración de configuración, clase & lt?& gt tipo de parámetro, mapeo & lt cadena, objeto & gt parámetros adicionales ) {
Super(configuración);
este . tipo de parámetro = tipo de parámetro;
este . meta parámetros = configuración nuevo meta (parámetros adicionales)
}
Lista pública & mapeo de parámetros& gtgetParameterMappings() {
Devolver mapeos de parámetros
}
Público; string handleToken(string content){
asignaciones de parámetros add(buildParameterMapping(content));
Return "?";
}
p. >//Esto es para analizar el contenido #{key} en una clase con una serie de atributos y luego establecer el valor mediante una serie de controladores de tipos.
Asignación de parámetros privados crear asignación de parámetros (contenido de cadena){
Map<String, String>properties map = parseParameterMapping(content);
propiedad de cadena = mapa de propiedades get(. " propiedad ");
Clase & lt? & gtpropertyType
if(meta parámetros. has getter(property)){//número 448 Obtener tipo de parámetros adicionales
tipo de propiedad = meta parámetros getgetter type(property);
} else if(typehandlerregistry . hastypehandler(tipo de parámetro)){
propertyType = tipo de parámetro
} else if (JdbcType.CURSOR.name().
es igual a (mapa de propiedades. get(" tipo JDBC ")){
tipo de propiedad = Java SQL . clase de resultados
} else if (propiedad! = nulo) {
Metaclase MetaClass = MetaClass . para clase(tipo de parámetro);
if(metaclase. tiene getter(propiedad)){
tipo de propiedad = metaclase. tipo(propiedad);
}else{
tipo de propiedad = Objeto.clase
}
}else{
propertyType = Object.class
}
Mapeo de parámetros. generador generador = nuevo mapa de parámetros. Constructor(configuración, propiedad, tipo de propiedad);
Clase & lt? & gtjavaType = propertyType
String typeHandlerAlias = null
For (Fig. Entrada & ltString, String & gtentry:properties map . conjunto de entrada()){
nombre de cadena = entrada . getkey();
valor de cadena = entrada valor();
if ("tipojava". Igual a (nombre)){
javaType = resolver clase(valor);
constructor . Tipo Java(tipo Java);
} else if ("jdbcType ". Igual a (nombre)){
constructor . JDBC tipo(resolveJdbcType(valor));
} else if ("modo ". es igual a (nombre)){
constructor . resolveParameterMode(valor) );
} else if ("numericScale ". igual a (nombre)){
constructor escala numérica (entero . valor de(valor));
} else if ("resultMap"). igual a (nombre)){
builder.resultMapId(valor);
} else if ("typeHandler ". igual a (nombre)){
typeHandlerAlias = valor
} else if ("jdbcTypeName ". Igual a (nombre)){
builder.jdbcTypeName(valor);
} else if ("Propiedad". igual a (nombre) {
//no hacer nada
} else if("expresión". igual a (nombre)){
throw new BuilderException("Los parámetros basados en expresiones aún no son compatibles");
}else{
throw new BuilderException("Se encontró un valor no válido en el mapeo #{" + contenido + "} Atributo ' "+ nombre +").
Las propiedades válidas son "+propiedades de parámetros);
}
}
if (typeHandlerAlias!= null) {
builder . type handler(resolveTypeHandler(tipo Java, typeHandlerAlias));
}
Devolver builder.build();
}
Mapa privado & ltString, String & gtparseParameterMapping(string content) {
Intenta {
Devolver nueva ParameterExpression(content);
} catch (BuilderException ex) { p>
Throw ex;
} catch (Exception ex) {
throw new BuilderException("Error de análisis encontrado en el mapeo #{" + contenido + "} . Verifique la sintaxis #{propiedad|(expresión), var1=valor1, var2=valor2,...}", ej);
}
}
}