MyBatis还是JPA?终于有答案了
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
MyBatis还是JPA?终于有答案了
对于⼀个和数据库打交道的程序员来说,很快会⾯临着⼀个艰难的选择。
到底是选择MyBatis还是JPA呢?
很多⼈说,技术选择,都要根据需求来,这个没错。
但是,除了需求,还有很重要的⼀个环节,那就是队友的⽔平。
如果你选择了⼀些⽐较⾼级的技术,那么就是在给整个团队埋坑。
JPA的抽象层次更⾼,代码写起来也更简洁,但是它⼀点都不简单。
虽然经过了多次的培训,我呆过的⼏个团队,还是把它⽤的和屎⼀样。
我扔掉了JPA
我仔细想了⼀下,有下⾯⼏点原因,造成了JPA在很多团队根本就玩不下去。
JPA适合业务模型固定的场景,适合⽐较稳定的需求。
但是国内这种朝三暮四的需求风格,产品经理这种传话筒式的设计模式,造成了需求的泛滥和不确定。
JPA在这种模式下就是渣。
JPA的技术要求⽐较⾼。
不要怀疑,你刚开始⽤起⾥可能觉得⾮常简单。
但随着你的深⼊使⽤,你会发现这是⼀个灾难。
⾥⾯的各种转换和缓存,会把⼈绕晕。
⽽⼤多数的快餐程序员是不想要了解这些的。
很多程序员很会写SQL,所以很多SQL语句长的很胖,长的要命。
业务混乱,多张表关联,我甚⾄见过上百张业务表关联的复杂业务。
DBA⽆奈之下,通常都会有sql审核。
JPA搞sql审核?还是弱了⼀点。
所以,不是JPA不好,⽽是它不符合国情⽽已。
想要在公司内推⾏JPA,你需要给我⼀个稳定的产品团队、⼀个⽜X的技术团队才⾏。
所以,⼤多数公司宁可写⼀堆重复的、乱七⼋糟的Mybaits代码,也不会轻易尝试JPA,这是符合逻辑的,符合事物发展规律的。
所以,我们下⾯的⽂章就是来讨论MyBatis的,来看⼀下Mybaits到底要怎么写才算优雅。
MyBatis为什么不好⽤
优秀的程序员都是很懒的。
所以很多⼈不想设计实体的sql。
JPA可以直接根据Java的实体代码,⽣成sql的库表,这在使⽤Mybatis的⼈来看,是⾮常羡慕的。
使⽤MyBatis,要倒着来。
需要先设计库表,然后根据库表反向⽣成⼀堆Java代码和配置⽂件。
这个代码⽣成器,就是mybatis-generator。
但是,请注意。
这个⽣成器⽣成的代码,有四种模式这就是最让初学者难受的地⽅。
如果你也是刚接触MyBatis,强烈推荐只关注下⾯第⼀种模式。
MyBatis3 这种模式就是我们常⽤的⽅式,会⽣成domain类、Example类、mapper映射⽂件等。
它⽣成的信息⽐较啰嗦,内容⼏乎⽆法改动。
对于项⽬中⾃⼰写的sql,⼀般都采⽤⼿写的⽅式再写⼀份,⽽不是改动原来的⽂件。
MyBatis3Simple 上⾯这种模式的简易代码⽣成模式,缺少⼀些东西,但很简洁。
对MyBatis没有经验,不推荐使⽤它。
MyBatis3DynamicSql 这是通过Builder模式实现的动态SQL特性,你还需要加⼊额外的jar包。
加上它之后,其实和JPA 是有点相似的。
既然如此,那为何不直接使⽤JPA呢?所以这个DSQL虽然是默认的⽣成⾏为,但是⾮常不推荐。
MyBatis3Kotlin 这个不废话。
就是⽣成Kotlin版的⼀些配置和代码信息。
所以,下⾯仅仅介绍MyBatis3模式的代码⽣成。
要使⽤它,需要在pom.xml⾥加⼊它的依赖。
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<optional>true</optional>
<scope>test</scope>
<version>1.4.0</version>
</dependency>
我个⼈喜欢使⽤Java代码来操作代码⽣成这个过程,所以下⾯就是⽣成代码的代码。
public class MBGTool {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<>();
boolean overwrite = true;
InputStream configFile = MBGTool.class.getResourceAsStream("/generator/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
从代码中,我们可以看到需要配置⼀个generatorConfig.xml⽂件,⽤来规定怎么⽣成代码⽂件。
<!DOCTYPE generatorConfiguration PUBLIC
"-////DTD MyBatis Generator Configuration 1.0//EN"
"/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="simple" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mbye"
userId="root"
password="root"
/>
<javaModelGenerator targetPackage="com.github.javarunfast.mbye.domain" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="com.github.javarunfast.mbye.mapper" targetProject="src/main/resources"/>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.github.javarunfast.mbye.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<table tableName="test"/>
</context>
</generatorConfiguration>
运⾏我们的MBGTool⽂件之后,就可以⽣成MyBatis的代码了。
怎么写代码最优雅
但是,我这⾥并不是要推荐你使⽤这种模式。
因为,它⽣成了⼀⼤堆⽆⽤的⽂件。
假如你的项⽬使⽤了sonar这样的代码质量审查⼯具,你会发现很多飘红的地⽅,还有那要命的覆盖率问题。
怎么办?
经过我多年的摸索,我现在推荐⼀种⾮常好⽤的写法。
⾃从我采⽤了这种⽅式之后,就再也没有换过。
第⼀、不需要代码⽣成器了
数据表的设计,还有domain的书写,全部靠⼿⼯。
这样我们的代码,如果有必要,还可以迁移到JPA上去。
这种模式还能顺便学习⼀下Java⾥⾯的数据类型,是如何和SQL⾥的数据类型⼀⼀对应的。
在做表设计的时候,顺便能够了解⼀些背后的原理。
第⼆、不需要写映射⽂件了
⽣成器⽣成的东西,确实是有⼀堆⽆⽤的逻辑。
⽐如我的某个数据表,根本不需要提供查询所有和删除这种动作,它还是默认提供了。
在这种简约模式下,我们直接⼿写Mapper⽂件,然后只声明所需要的接⼝⽅法就可以了。
@Mapper
public interface AccountBasicMapper {
@Insert("AccountBasicMapper/insert.sql")
void insert(@Param("r") AccountBasic record);
}
可以看到,⾥⾯有⼀个Insert注解,我们传⼊了⼀个具体的domain,然后,就可以在AccountBasicMapper⽬录下的insert.sql ⽂件⾥,书写具体的sql语句了。
sql语句样例如下:
INSERT INTO account_basic(
account_id,
nick_name,
password,
sex,
state,
photo_url,
created,
modified,
version
)VALUES (
<@p name="r.accountId"/>,
<@p name="r.nickName"/>,
<@p name="r.password"/>,
<@p name="r.sex"/>,
<@p name="r.state"/>,
<@p name="r.photoUrl"/>,
<@p name="r.created"/>,
<@p name="r.modified"/>,
<@p name="r.version"/>
)
那么这是什么语法呢?它⼜是如何知道是这样配置的呢?这就需要引⼊MyBatis的脚本语⾔配置功能。
在这⾥,我们使⽤的freemark的模版。
不要忘了加⼊它的依赖。
<dependency>
<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-freemarker</artifactId>
<version>1.2.2</version>
</dependency>
然后,在yaml⽂件⾥做上相应的配置就ok了。
mybatis:
check_config_location: false
scripting-language-driver:
freemarker:
template-file:
base-dir: mappers/
path-provider:
includes-package-path: false
separate-directory-per-mapper: false
这种⽅式的好处和坏处
我个⼈是⾮常喜欢这种模式的。
因为它有下⾯⼏个好处:
⽤什么写什么,代码量少,简洁优雅。
SQL集中,不⽤分散在代码⾥,xml⾥,或者注解⾥。
⽅便DBA进⾏SQL审核。
由于没了xml的⼲扰,SQL反⽽更加简洁了。
⼀个DAO⽅法⼀个sql⽂件,模式单⼀可控。
MyBatis的功能优势可以全部发挥,⽆缝集成。
当然,缺点也是显⽽易见的。
即使变了个参数,也要修改很多sql⽂件。
需要为每⼀个⽅法配⼀个sql⽂件,即使这是个很弱智的插⼊查询⽅法。
不过,我并不认为这是个问题。
每⼀个⽅法配备⼀个sql⽂件,代码写起来反⽽更加简单了。
当出现问题的时候,也不⽤根据逻辑进⾏跟踪定位到拼接后的SQL语句。
我现在,只需要拿到对应⽅法的SQL⽂件,就可以改吧改吧,直接在sql终端⾥执⾏调试。
这样,sql优化也变的简单了。
当然,⼀个⼈⼀个习惯。
我个⼈喜欢这种模式,⽽且在我的团队⾥推⾏这种模式,发现运⾏的也很好。
另外,程序员为了少写重复的sql代码,在设计Dao接⼝的时候,反⽽更加认真了。
这可能是⼀个额外的收获吧。
到此这篇关于MyBatis还是JPA?终于有答案了的⽂章就介绍到这了,更多相关MyBatis还是JPA内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。