2017-02-20 2 views
2

MyBatis 사용법을 배우고 있습니다. 솔직히 나는이 프레임 워크를 아주 좋아합니다. 사용하기 쉽고 SQL 명령을 사용할 수 있기 때문에 만족합니다 :) MyBatis 3.4.2 및 PostgreSQL 데이터베이스를 사용합니다.MyBatis로 입력 및 출력 매개 변수 매핑하기

예를 들어, @SelectKey 주석을 삽입하기 전에 쿼리를 실행하는 것이 얼마나 쉬운 지 알고 싶습니다. 인터페이스 메소드 앞에 몇 가지 주석을 추가하면 데이터 매핑이 매력적으로 작동합니다 (@Results({ @Result(property = "javaField", column = "database_field", javaType = TypeHandler.class)). 내가 좋아하지 않아 (그리고 난 당신이 올바른 방향으로 날을 넣을 수 있기를 바랍니다) 무엇

다음과 같습니다


(문제 1)에 저를 허용하는 나는 질문이 추가 "if"java 문없이 널값과 일반 값을 사용하여 변수에 널값 또는 널값이 들어 있는지 점검하십시오. 그들은 다음과 같습니다 : JDBC와

SELECT * FROM table 
WHERE key_name = ? AND ((? IS NULL AND user_id IS NULL) OR User_id = ?) 

나는 다음에 필요 당신이 볼 수 있듯이

stmt = connection.prepareStatement(query); 
stmt.setString(1, "key"); 
stmt.setString(2, userId); 
stmt.setString(3, userId); 

내가이 JDBC가 어떻게 작동하는지 방법이기 때문에 두 번 userId를 통과해야합니다. 솔직히 내 기대는 다음 코드가 MyBatis와 함께 작동하지만 불행히도 작동하지 않는다는 것이 었습니다. 세 번째 매개 변수는 여전히 정의되어야합니다.

MyBatis에이 기능을 추가 할 수 있는지 궁금합니다. 실제로 내가 무슨 짓을 해결

@Select("SELECT * FROM table key_name = #{key} and ((#{userId} is null and user_id is null) OR user_id = #{userId}) 
SomeClass findByKeyAndUserId(String key, Long userId); 

은 다음과 같다 : MyBatis로이 같은 두 번 자동으로 userId를, 뭔가를 결합 할 수 있다면 그건 잘해야한다. 이 문이 필요하다 "만일"까다 롭고 추가 자바 있기 때문에 나는 그것을 싫어 :

@Select("SELECT * FROM table WHERE key_name = #{key} AND COALESCE(user_id, -1) = #{userId}") 
SomeClass findByKeyAndUserId(String key, Long userId); 

userId = (userId == null) ? -1 : userId; 
SomeClass abc = mapper.findByKeyAndUserId(key, userId); 

내가 MyBatis로이 상황을 관리 할 수있는 가장 좋은 방법이 무엇인지 모른다. 나를 안내 해줘. @Select 경우


(문제 2) 매핑. 동일한 결과 유형의 쿼리 결과를 매핑하는 동안 반복되는 코드를 피할 수있는 방법이 있습니까?

1 쿼리 :

@Select("SELECT * FROM table WHERE ...") 
@Results({ 
     @Result(property = "key", column = "key_name", javaType = String.class), 
     @Result(property = "value", column = "key_value", javaType = String.class), 
     @Result(property = "userId", column = "user_id", javaType = Long.class), 
     @Result(property = "interval", column = "interval", javaType = Long.class), 
     @Result(property = "description", column = "description", javaType = String.class), 
     @Result(property = "status", column = "status", typeHandler = StatusTypeHandler.class) 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

2 쿼리

@Select("SELECT * FROM table WHERE <different conditions then before>") 
@Results({ 
     <I need to add here the exact same code then before in query 1> 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

은 어떻게 든 매핑 관련 코드를 재사용 할 수 있습니까? 상태 필드에 특수 유형 핸들러를 사용하므로 맵핑을 추가해야합니다. 주석 기반 구성을 사용합니다.


(문제 3)@Param 주석 나는이 문서에 대한 @Param 주석에서 아무것도 볼 수 없었다. 내 Java 매개 변수가 제대로 묶이지 않은 이유를 파악하기가 어려웠습니다.마지막으로 @Param 주석이 제 코드에서 누락되었습니다. 이것이 공식 문서에서 언급되지 않은 이유는 무엇입니까? 나는 잘못된 방법으로 뭔가를했고 @Param은 사용할 필요가 없습니다.

는 코드가 잘 작동하는지 :

SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

이 작동하지 않습니다

SomeClass findByKeyAndUserId(String key, Long userId); 

UPDATE

내 첫번째 생각은 그것과 유사했다 (문제 1)의 경우 @blackwizard menti oned : "Mybatis는 매개 변수를 이름으로 바인딩 한 다음 한 번, 두 번, N 번, 참조 할 때와 같이 작동합니다."

하지만 실제로는 제대로 작동하지 않습니다. userId가 null이 아닌 경우 작동합니다. null 인 경우 데이터베이스에서 돌아 오는 멋진 예외가 발생합니다. 내 생각에 MyBatis는 잘못된 값으로 null 값을 바인딩합니다. 어쩌면 그것은 버그 일 것입니다. 나도 몰라 :(

예외 :

마지막으로
org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $2 
### The error may exist in com/.../dao/TableDao.java (best guess) 
### The error may involve ....dao.Table.findByKeyAndUserId-Inline 
### The error occurred while setting parameters 
### SQL: SELECT * FROM table WHERE key_name = ? AND (? IS NULL AND user_id IS NULL) OR user_id = ? AND status = 1 
### Cause: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $2 

내가 찾은 세 가지 솔루션을

(문제 1 : 솔루션 1).

내가 다음에 무엇을 @blackwizard 언급했는데 java에서 MyBatis 수준으로 userId = (userId == null) ? -1 : userId 조건을 옮길 수있었습니다. 그리고 나쁘지 않습니다! 올바른 구문은입니다..

(문제 1 : 솔루션 2) null 값의 경우 JDBC 드라이버가 매개 변수의 유형을 판별 할 수 없기 때문에 내가 포스트 그레스에서 다시 could not determine data type of parameter $2 오류가 발생하는 이유입니다. 그럼 수동으로 정의 해 봅시다.

@Select("SELECT * FROM table " 
     + "WHERE key_name = #{key} AND ((#{userId}::BIGINT IS NULL AND user_id IS NULL) OR user_id = #{userId})") 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

(문제 1 : 용액 3) 제 2 용액 PorstgreSQL에 의존한다. 다음 솔루션은 전적으로 데이터베이스 독립적입니다. 좋은 의견에 대한 @blackwizard 주셔서 감사합니다.

@Select("SELECT * FROM table " 
     + "WHERE key_name = #{key} AND ((#{userId, jdbcType=BIGINT} IS NULL AND user_id IS NULL) OR user_id = #{userId})") 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

개인적으로 나는 솔루션 3을 선호합니다. 추가 코드가 적습니다.

답변

4

문제 1 :

이름 매개 변수 :이 참조하는 시간이 있으므로 이름으로

@Select("SELECT * FROM table key_name = #{key} and ((#{userId} is null and user_id is null) OR user_id = #{userId}") 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

MyBatis로 바인딩 않는 매개 변수는 다음 1 회, 2 회, N 시간은, 그것을 작동합니다.

Mybatis에서 인 경우 을 수행 할 수 있습니다. XML 태그를 사용하면 더 좋지는 않지만 어쨌든 다른 용도로 언젠가 재사용 할 수 있습니다.주석 값에서 XML 태그를 사용하려면 문자열의 처음과 끝에 정확하게 <script> 태그에 값을 삽입해야합니다. #{userId, typeHandler=CustomDefaultValueTypeHandler}

편집 : 대답 추가 질문에 :

@Select({"<script>", 
     "<if 'userId==null'><bind name='userId' value='1'/></if>", 
     "SELECT * FROM table WHERE key_name = #{key} ", 
     "AND COALESCE(user_id, -1) = #{userId}", 
     "</script>"}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

또한 단지 매개 변수와 함께 사용, 기본 값을 설정 typeHandler를 사용할 수 있습니다 대신 처리의 널 (null) 값을 전달 허용하려면 대신에 Mapper 인터페이스의 변수 선언을 모르거나 알지 못하기 때문에 null의 실제 유형을 해석 할 수 없기 때문에 Mybatis에 바인딩 된 매개 변수 유형에 대한 힌트를 제공해야합니다. 그래서 : #{userId, javaType=int,jdbcType=NUMERIC}. 두 속성 중 하나만 있으면 충분합니다.

Documentation 상태 : 그 객체가 의 HashMap하지 않는 한 MyBatis로의 나머지처럼

만약 javaType 거의 항상 파라미터 객체에서 를 결정할 수있다. 그런 다음 java 유형을 지정하여 올바른 TypeHandler이 인지 확인해야합니다.

참고 null 값이 전달되는 경우 모든 null 가능 열에 대해 JDBC에서 JDBC 유형이 필요합니다. PreparedStatement.setNull() 메소드에 대한 JavaDocs를 읽고 으로 직접 조사 할 수 있습니다.

문제 2 : 당신은 확실히 당신이 주석에서 정의하는 것을 mutualize/다시 사용할 수 없습니다. 이것은 Mybatis 때문이 아니라 주석입니다. 결과 맵을 @ResultMap으로 참조하는 XML로 정의해야합니다. 이미 answered in your other question, 파라미터 : 주석 같은 맵에 편리한 매개 변수 목록을 변환 할 수있는 효과를 가지고 : Documentation 상태 :

ResultMap Method N/A  This annotation is used to provide the id of a <resultMap> element in an XML mapper to a @Select or @SelectProvider annotation. This allows annotated selects to reuse resultmaps that are defined in XML. This annotation will override any @Results or @ConstructorArgs annotation if both are specified on an annotated select. 

문제 3

Map<String, Object> params = new HashMap<String, Object>(); 
params.put("key", key); 
params.put("userId", userId); 

을 나는 동의의 MyBatis 문서 더 좋을 수 있고 here와 같은 더 많은 리소스를 찾을 수 있습니다.

그러나 Mybatis documentation 상태가

@Param Parameter N/A  If your mapper method takes multiple parameters, this annotation can be applied to a mapper method parameter to give each of them a name. Otherwise, multiple parameters will be named by their position prefixed with "param" (not including any RowBounds parameters). For example #{param1}, #{param2} etc. is the default. With @Param("person"), the parameter would be named 
#{person}. 
+0

답변 해 주셔서 감사합니다. 나는 (문제 1)에 대한 나의 원래 게시물을 업데이 트했습니다.확인해 주시겠습니까? 내일 또 다른 두 가지 대답을 확인하겠습니다. – zappee

+0

3 번째 문제는 분명합니다. 정말 고마워! 그래서 예외 로그에서 때때로 paramX 바인딩이 올바르지 않은 것을 볼 수있는 이유입니다. – zappee

+0

내 게시물을 업데이트했습니다. 첫 번째 문제는 완전히 해결되었습니다. 당신의 도움을 주셔서 감사합니다! – zappee

0

문제에 대한 @Param 주석 3 다음

것은 -parameters 당신이 파라미터 : 주석을 생략 할 수 있습니다 JDK 8 부터 제공 컴파일 옵션을 시도하십시오.

https://github.com/mybatis/mybatis-3/issues/549

감사를 참조하십시오.