[Java Spring] [QueryDSL-SQL] QueryDSL-SQL SQLTemplates 설정 문제로 인한 생성 쿼리 Grammar Error
QueryDSL-SQL SQLTemplates 설정 문제 해결
문제 상황
- QueryDSL을 사용하여 데이터베이스 쿼리를 생성할 때, 생성 쿼리에 Bad sql grammar error가 발생하는 문제가 발생하였다.
- 특히 where절에서 boolean 조건이 데이터베이스 벤더에 맞지 않는 형식으로 생성되는 현상이 있었다.- 예를 들어, PostgreSQL에서는 true/false로 처리되어야 하는데1/0으로 처리되는 등의 불일치가 생겼다.
 
- 예를 들어, PostgreSQL에서는 
원인 정의
- 
    SQLTemplates설정이 데이터베이스 벤더별 특성을 제대로 반영하지 못하였다.com.querydsl.sql.Configuration configuration = new com.querydsl.sql.Configuration(SQLTemplates.DEFAULT);위와 같이 Config를 설정하면, 각기 다른 DB 벤더를 구동하도록 설정하였을때 런타임에 알아서 적절한 SQLTemplate객체를 선택해 작동하는 줄 알았는데 아니었다. 결국 템플릿 설정은 직접 해줘야 하는 거였다. 
- 
    알 수 없는 데이터베이스 벤더에 대한 fallback 처리가 부적절 - SQLTemplates를 직접 인스턴스화하려 시도했으나, 이는 추상 클래스이므로 불가능하다.
 
따라서 런타임에 벤더 metadata 정보를 감지해 이에 따라 SQLTemplates를 선택하도록 구현하였다.
해결 방안
- 벤더별 SQLTemplates생성자 파라미터 활용
- 데이터베이스 자동 감지 로직 구현 (DatabaseMetaData)
- 안전한 Fallback 처리
    - 알 수 없는 데이터베이스 벤더나 오류 발생 시 H2Templates를 기본값으로 사용하도록 하였다.
 
- 알 수 없는 데이터베이스 벤더나 오류 발생 시 
- 추가 설정
    - Exception Translator 설정으로 스프링의 예외 처리 통합
- Literal 사용 설정으로 prepared statement 대신 직접 값을 쿼리에 포함시키도록 하였다.
 
다음은 위 사항을 반영한 QueryDSL-SQL Config 파일의 일부이다.
	@Bean  
	public SQLQueryFactory sqlQueryFactory(DataSource dataSource) {  
	    SQLTemplates templates = createSQLTemplates(dataSource);  
	    com.querydsl.sql.Configuration configuration = new com.querydsl.sql.Configuration(templates);  
	    configuration.setExceptionTranslator(new SpringExceptionTranslator());  
	    configuration.setUseLiterals(true);  
	      
	    return new SQLQueryFactory(configuration, new SpringConnectionProvider(dataSource));  
	}  
	  
	private SQLTemplates createSQLTemplates(DataSource dataSource) {  
	    try (Connection conn = dataSource.getConnection()) {  
	        DatabaseMetaData metaData = conn.getMetaData();  
	        String databaseProductName = metaData.getDatabaseProductName().toLowerCase();  
	          
	        log.info("Detected database: {}", databaseProductName);  
	          
	        if (databaseProductName.contains("postgresql")) {  
	            return new PostgreSQLTemplates(true);  // true for quote identifiers  
	        } else if (databaseProductName.contains("mysql")) {  
	            return new MySQLTemplates(true);  
	        } else if (databaseProductName.contains("oracle")) {  
	            return new OracleTemplates(true);  
	        } else if (databaseProductName.contains("h2")) {  
	            return new H2Templates(true);  
	        }  
	          
	        log.warn("Unknown database vendor: {}. Using H2Templates as default", databaseProductName);  
	        return new H2Templates(true);  
	          
	    } catch (SQLException e) {  
	        log.error("Failed to detect database vendor. Using H2Templates as default", e);  
	        return new H2Templates(true);  
	    }  
	}
결과
- 데이터베이스 벤더별로 적절한 boolean 값 처리가 가능해졌다.
- 자동으로 데이터베이스 타입 감지 및 적절한 템플릿 적용이 가능해졌다.
- 예외 상황에 대한 안전한 폴백 처리를 구현하였다.
이러한 설정을 통해 QueryDSL-SQL을 통해 런타임에 각 데이터베이스를 감지하고, 벤더에 적절한 SQL을 생성하도록 보장할 수 있게 하였다.
Leave a comment