前段時間了解到Spring JPA,感覺挺好用,但其依賴於Hibernate,本人看到Hibernate就頭大(不是說Hibernate不好哈,而是進階太難),於是做了一個迷你版的Mybatis JPA.
代碼地址(github): https://github.com/svili365/mybatis-jpa
代碼地址(gitee): https://gitee.com/svili/mybatis-jpa
QQ交流群:246912326
因為版本更新,可能會導致博客與代碼不對應,強烈建議閱讀代碼倉庫的wiki文檔
maven
<dependency> <groupId>com.littlenb</groupId> <artifactId>mybatis-jpa</artifactId> <version>2.1.1</version> </dependency>
1.1 版本說明
v2.0.1:純凈版的resultTypePlugin
v2.1.0:在v2.0.1基礎上,增加SQL構建,InsertDefinition|UpdateDifinition
v2.1.1:增加自定義枚舉值,ICodeEnum,@CodeEnum,IntCodeEnumTypeHandler,StringCodeEnumTypeHandler
1.2 工作模式
1.)mybatis-jpa 是基於Mybatis的增強插件,沒有對依賴包(源代碼)造成污染.
2.)ResultTypePlugin在運行時攔截,每個被攔截的方法會在初次調用時完成解析.
3.)mybatis-jpa SQL的解析和Statement的注冊時機,是在Spring applicationContext初始化完成時,只會解析一次.
4.)由mybatis-jpa 解析的Mapper接口中定義的方法(method),將被注冊到Mybatis Configuration中,即Mapper的代理和注入由依舊由Mybatis和Spring構建和管理,不影響原有的代碼模式和工作模式.
1.3 約定
1.)Entity實體類需使用@Entity或@Table注解標記,類中字段類型不允許使用基本數據類型(如:使用Integer定義整形而不是int);
2.)ResultTypePlugin支持結果集的嵌套,SQL的構建(InsertDefinition|UpdateDifinition)會忽略實體類的嵌套.
3.)按照Mybatis約定,Enum枚舉類型默認以 enum.name() 解析,若要解析為enum.ordinal(),需使用注解@Enumrated(value = EnumType.ORDINAL)標識.
4.)使用自定義枚舉值,枚舉類型需實現ICodeEnum接口,並使用注解@CodeEnum標記Field.@CodeEnum優先級高於@Enumrated.
插件清單
-
ResultTypePlugin
-
DefinitionStatementScanner
2.1 ResultTypePlugin
對於常規的結果映射,不需要再構建ResultMap,ResultTypePlugin增加了Mybatis對結果映射(JavaBean/POJO)中JPA注解的處理。
映射規則:
-
名稱匹配默認為駝峰(Java Field)與下划線(SQL Column)
-
使用@Column注解中name屬性指定SQL Column
-
使用@Transient注解標記非持久化字段(不需要結果集映射的字段)
類型處理:
-
Boolean-->BooleanTypeHandler
-
Enum默認為EnumTypeHandler
使用@Enumerated(EnumType.ORDINAL) 指定為 EnumOrdinalTypeHandler
-
Enum實現ICodeEnum接口實現自定義枚舉值
使用@CodeEnum(CodeType.INT) 指定為 IntCodeEnumTypeHandler
或@CodeEnum(CodeType.STRING) 指定為 StringCodeEnumTypeHandler
@CodeEnum 優先級 高於 @Enumerated
結果集嵌套:
- 支持OneToOne
- 支持OneToMany
e.g.
mybatis.xml
<configuration> <plugins> <plugin interceptor="com.mybatis.jpa.plugin.ResultTypePlugin"> </plugin> </plugins> </configuration>
JavaBean
@Entity public class UserArchive {// <resultMap id="xxx" type="userArchive"> @Id private Long userId;// <id property="id" column="user_id" /> /** 默認駝峰與下划線轉換 */ private String userName;// <result property="username" column="user_name"/> /** 枚舉類型 */ @Enumerated(EnumType.ORDINAL) private SexEnum sex;// <result property="sex" column="sex" typeHandler=EnumOrdinalTypeHandler/> /** 枚舉類型,自定義值 */ @CodeEnum(CodeType.INT) private PoliticalEnum political;// <result property="political" column="political" typeHandler=IntCodeEnumTypeHandler/> /** 屬性名與列名不一致 */ @Column(name = "gmt_create") private Date createTime;// <result property="createTime" column="gmt_create"/> }// </resultMap>
mapper.xml
<!-- in xml,declare the resultType --> <select id="selectById" resultType="userArchive"> SELECT * FROM t_sys_user_archive WHERE user_id = #{userId} </select>
DefinitionStatementScanner
注冊MappedStatement,基於注解,僅支持Insert和Update。
@InsertDefinition:
- selective: 默認值false(處理null屬性)
@UpdateDefinition:
-
selective: 默認值false(處理null屬性)
-
where: SQL condition
e.g.
Spring 容器初始化完成后執行
@Service public class DefinitionStatementInit { @Autowired private SqlSessionFactory sqlSessionFactory; @PostConstruct public void init() { Configuration configuration = sqlSessionFactory.getConfiguration(); StatementBuildable statementBuildable = new DefinitionStatementBuilder(configuration); DefinitionStatementScanner.Builder builder = new DefinitionStatementScanner.Builder(); DefinitionStatementScanner definitionStatementScanner = builder.configuration(configuration).basePackages(new String[]{"com.mybatis.jpa.mapper"}) .statementBuilder(statementBuildable).build(); definitionStatementScanner.scan(); } }
Mapper
@Mapper @Repository public interface UserUpdateMapper { @InsertDefinition(selective = true) int insert(User user); @UpdateDefinition(selective = true, where = " user_id = #{userId}") int updateById(User user); }
更多示例請查看test目錄代碼。
如果你想深入了解,項目代碼目錄還算清晰,源碼中有大量必要的注釋,你會發現有部分英文注釋,不要慌,是我寫的,現在感覺有些代碼用英文描述反而會簡單一些,用中文反而不能夠被很好的理解.
代碼的構建思路及代碼解析,見博文:http://www.cnblogs.com/svili/p/7232323.html
因個人能力有限,如有不足之處,請多包涵,歡迎交流/指正.