在Repository接口中定义的方法,有时候根据业务需要会自定义自己的sql语句,如@Query(“select * from table where create_time > (now() - (:overTime||‘min’)::::interval)”, nativeQuery = true),框架对这个sql语句的解析会把双 【::】 识别为单个【:】,对这个sql语句的解析放在这个ParameterParser.java类中,所以我们写成::::interval而不能写成::interval
以下为解析的核心代码:
// org.hibernate.engine.query.spi.ParameterParser.java
public static void parse(String sqlString, Recognizer recognizer) throws QueryException {
final boolean hasMainOutputParameter = startsWithEscapeCallTemplate( sqlString );
boolean foundMainOutputParam = false;
final int stringLength = sqlString.length();
boolean inSingleQuotes = false;
boolean inDoubleQuotes = false;
boolean inLineComment = false;
boolean inDelimitedComment = false;
for ( int indx = 0; indx < stringLength; indx++ ) {
final char c = sqlString.charAt( indx );
final boolean lastCharacter = indx == stringLength-1;
// if we are "in" a certain context, check first for the end of that context
if ( inSingleQuotes ) {
recognizer.other( c );
if ( '\'' == c ) {
inSingleQuotes = false;
}
}
else if ( inDoubleQuotes ) {
recognizer.other( c );
if ( '\"' == c ) {
inDoubleQuotes = false;
}
}
else if ( inDelimitedComment ) {
recognizer.other( c );
if ( !lastCharacter && '*' == c && '/' == sqlString.charAt( indx+1 ) ) {
inDelimitedComment = false;
recognizer.other( sqlString.charAt( indx+1 ) );
indx++;
}
}
else if ( inLineComment ) {
recognizer.other( c );
// see if the character ends the line
if ( '\n' == c ) {
inLineComment = false;
}
else if ( '\r' == c ) {
inLineComment = false;
if ( !lastCharacter && '\n' == sqlString.charAt( indx+1 ) ) {
recognizer.other( sqlString.charAt( indx+1 ) );
indx++;
}
}
}
// otherwise, see if we start such a context
else if ( !lastCharacter && '/' == c && '*' == sqlString.charAt( indx+1 ) ) {
inDelimitedComment = true;
recognizer.other( c );
recognizer.other( sqlString.charAt( indx+1 ) );
indx++;
}
else if ( '-' == c ) {
recognizer.other( c );
if ( !lastCharacter && '-' == sqlString.charAt( indx+1 ) ) {
inLineComment = true;
recognizer.other( sqlString.charAt( indx+1 ) );
indx++;
}
}
else if ( '\"' == c ) {
inDoubleQuotes = true;
recognizer.other( c );
}
else if ( '\'' == c ) {
inSingleQuotes = true;
recognizer.other( c );
}
// special handling for backslash
else if ( '\\' == c ) {
// skip sending the backslash and instead send then next character, treating is as a literal
recognizer.other( sqlString.charAt( ++indx ) );
}
// otherwise
else {
if ( c == ':' && indx < stringLength - 1 && sqlString.charAt( indx + 1 ) == ':') {
// colon character has been escaped
recognizer.other( c );
indx++;
}
else if ( c == ':' ) {
// named parameter
final int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS_BITSET, indx + 1 );
final int chopLocation = right < 0 ? sqlString.length() : right;
final String param = sqlString.substring( indx + 1, chopLocation );
if ( param.isEmpty() ) {
throw new QueryException(
"Space is not allowed after parameter prefix ':' [" + sqlString + "]"
);
}
recognizer.namedParameter( param, indx );
indx = chopLocation - 1;
}
else if ( c == '?' ) {
// could be either an ordinal or JPA-positional parameter
if ( indx < stringLength - 1 && Character.isDigit( sqlString.charAt( indx + 1 ) ) ) {
// a peek ahead showed this as a JPA-positional parameter
final int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, indx + 1 );
final int chopLocation = right < 0 ? sqlString.length() : right;
final String param = sqlString.substring( indx + 1, chopLocation );
// make sure this "name" is an integral
try {
recognizer.jpaPositionalParameter( Integer.parseInt( param ), indx );
indx = chopLocation - 1;
}
catch( NumberFormatException e ) {
throw new QueryException( "JPA-style positional param was not an integral ordinal" );
}
}
else {
if ( hasMainOutputParameter && !foundMainOutputParam ) {
foundMainOutputParam = true;
recognizer.outParameter( indx );
}
else {
recognizer.ordinalParameter( indx );
}
}
}
else {
recognizer.other( c );
}
}
}
recognizer.complete();
}