最近工作比较闲,维护一个政府机关的短信发送平台,大部分业务逻辑都在Oracle数据库上,但自己明明应聘的是Java开发啊!!!整天写存储过程的我还是有一颗写高级语言的心啊!!!好吧!!!先找个数据库方面的框架学起来吧!
手头项目比较老,还在用ibatis,就找了它的后继者Mybatis3来学习(3.3.0 released,24 May 2015)。
学习的期望是:
- 理解Mybatis相比JDBC最大的优点。
- 自己动手搭建Mybatis框架。
从这几个部分理解:
- Mybatis与ibatis的不同
- XML配置
- XML映射文件
- 动态SQL
没耐心的可以直接跳到最后看总结。
找来官方文档(还好有中文的)就读起来。
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。我们可以从 SqlSessionFactory 中获取 SqlSession ,SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
我认为 SqlSessionFactory 相当于 ibatis中的SqlMapClient ,因为后者的方法中也有一个openSession()。SqlSession 相当于ibatis中的 SqlMapSession。
相比之下,Mybatis采用更清晰并且类型安全的方式来执行SQL映射。
1 SqlSession session = sqlSessionFactory.openSession();2 try {3 BlogMapper mapper = session.getMapper(BlogMapper.class);4 Blog blog = mapper.selectBlog(101);5 } finally {6 session.close();7 }
对于简单的增删改查,这只需要在Java中添加注解(复杂的映射文档中说了最好还是在XML中定义)
1 package org.mybatis.example;2 public interface BlogMapper {3 @Select("SELECT * FROM blog WHERE id = #{id}")4 Blog selectBlog(int id);5 }
当然,在ibatis中通过XML定义SQL语句映射的方式也是支持的。
XML映射文件是mybatis的重点。
1 <select id="selectPerson" parameterType="int" resultType="hashmap">2 SELECT * FROM PERSON WHERE ID = #{id}3 </select>
跟ibatis很像有木有~不同的是这里用#{parameter}来表示映射字段 而ibatis中是#patameter#。而在JDBC中,你需要像这样来映射字段,并且在得到结果集后一一手动映射到对象。这就是Mybatis框架可以节省代码的地方。
在JDBC中是这样子的:
1 String selectPerson = "SELECT * FROM PERSON WHERE ID=? and name = ? and age = ?"; 2 PreparedStatement ps = conn.prepareStatement(selectPerson); 3 ps.setInt(1,id); 4 ps.setString(2,name); 5 ps.setInt(3,age); 6 ResultSet res = ps.executeQuery(); 7 while(res.next()){ 8 Student stu = new Student(); 9 stu.setId(res.getInt(1));10 stu.setName(res.getString(2));11 stu.setAge(res.getInt(3));12 }
而Mybatis中是这样子的:
1 <typeAliases>2 <typeAlias alias="stu" type="domain.Student"/>3 </typeAliases>4 <select id="selectStudent" resultType="stu">5 select id, username, password6 from users7 where id = #{id}8 </select>
除去标签占去的代码行数,当映射对象的属性更多时,代码量差距会更加明显。
select中可以设置属性,常用的就是id(命名空间中的唯一标识符,用来被引用)、parameterType(传入参数的完整包名或别名)、resultType(返回结果的完整报名或别名,不和resultMap同时使用)、resultMap(外部resultMap的命名引用,稍后会详细讲)、flushCache(清空缓存)、useCache(使用缓存)、timeout(等待数据库返回结果的最大时间)
对于Oracle,一般在插入中都需要获得主键,可以使用<selectKey>标签来获得主键ID。
1 <!--Mybatis中Oracle的主键使用方法 --> 2 <insert id="insertSMSInpojo" parameterType="SMSINPojo"> 3 <selectKey keyProperty="id" resultType="int"> 4 select s_sms_in.nextval from dual 5 </selectKey> 6 insert into sms_in( 7 msgContent, 8 mobile, 9 destNO,10 linkId,11 curTime12 ) values(13 #msgContent#,14 #mobile#,15 #destNO#,16 #linkId#,17 #curTime#18 )19 </insert>
对于参数映射,Mybatis中提供了强大的resultMap来支持。对于简单的查询(列名与属性名一一对应),你可以使用hashmap或pojo类来实现
1 <!-- In mybatis-config.xml file --> 2 <typeAlias type="com.someapp.model.User" alias="User"/> 3 4 <!-- In SQL Mapping XML file --> 5 <select id="selectUsers" resultType="User"> 6 select id, username, hashedPassword 7 from some_table 8 where id = #{id} 9 </select>10 11 or use this 12 13 <select id="selectUsers" resultType="map">14 select id, username, hashedPassword15 from some_table16 where id = #{id}17 </select>
如果列明没有精确匹配,可以通过 as 来设置别名
1 <select id="selectUsers" resultType="User">2 select3 user_id as "id",4 user_name as "userName",5 hashed_password as "hashedPassword"6 from some_table7 where id = #{id}8 </select>
或是通过更强大的resultMap来解决,因为实现了列名和属性名分离,更易于在不同语句中重用。
1 <resultMap id="userResultMap" type="User"> 2 <id property="id" column="user_id" /> 3 <result property="username" column="user_name"/> 4 <result property="password" column="hashed_password"/> 5 </resultMap> 6 7 <select id="selectUsers" resultMap="userResultMap"> 8 select user_id, user_name, hashed_password 9 from some_table10 where id = #{id}11 </select>
而且不仅是代码量的差距,我也相信你一定维护过像这样 逗号,拼接 的代码:
1 public void insertIntoHddx(TbSmsSync tbsyncpojo, SmsTaskPojo smstask, int num, String optionList) { 2 String hddxSql = "insert into sms_hddx values("; 3 hddxSql += tbsyncpojo.getTaskId() + ","; 4 hddxSql += "'" + smstask.getTaskName() + "',"; 5 hddxSql += "'" + tbsyncpojo.getMsgContent() + "',"; 6 hddxSql += "0 ,"; 7 hddxSql += "to_date('" + smstask.getSendTime() + "','yyyy-mm-dd hh24:mi:ss') ,"; 8 hddxSql += num + ")"; 9 String[] items = optionList.split(";");10 for (String item : items) {11 String[] itemIdAndName = item.split(",");12 String itemId = itemIdAndName[0];13 String itemName = itemIdAndName[1];14 String searchItemSql = "insert into sms_hddx_option values(";15 searchItemSql += "'" + itemId + "',";16 searchItemSql += "'" + itemName + "',";17 searchItemSql += "0,";18 searchItemSql += tbsyncpojo.getTaskId() + ")";19 JdbcTemplate template = dbTemplate.getJdbcTemplate();20 template.execute(searchItemSql);21 }22 JdbcTemplate template = dbTemplate.getJdbcTemplate();23 template.execute(hddxSql);24 }
或是这样 #动态字段映射# 的代码
1 public PageInfo showTasks(SearchSMS searchSMS, int page) { 2 String sql = "select id from SMS_TASK sms_task where id = ? " 3 } 4 if (StringUtils.isNotBlank(searchSMS.getEndtime())) { 5 sql += "and createtime < to_date('" + searchSMS.getEndtime() + "','yyyy-mm-dd') + 1 "; 6 } 7 if (StringUtils.isNotBlank(searchSMS.getSendContent())) { 8 sql += "and id in (select TASKID from SMS_SYNC where TASKID=sms_task.id and msgcontent like '%" + searchSMS.getSendContent() + "%')"; 9 }10 if (StringUtils.isNotBlank(searchSMS.getSendPhone())) {11 sql += "and id in (select TASKID from SMS_SYNC where TASKID=sms_task.id and mobile like '%" + searchSMS.getSendPhone() + "%')";12 }
你一定感受过为了一个逗号而抓狂的经历,拼接的时候提醒自己一定不能忘了添加前后的空格和删除句末的逗号。
幸运的是,通过动态SQL这样的问题在Mybatis中不复存在。通过<where>来智能识别语句前后的and/or,<set>来识别语句前后的逗号,或使用<trim>来自定义实现。
通过<if test>来判断字段是否为空,<choose><when><otherwise>来实现类似switch……case的效果,<foreach>来实现集合的遍历,这里介绍了动态SQL。
总结:Mybatis框架优越之处在于使用XML映射文件替代了JDBC中手工设置属性,降低SQL代码耦合性,将动态SQL与其他Java代码分离,使得代码看起来更加整洁并有利于重用;提供缓存、自动映射功能,并通过一系列设置合理的默认值来简化开发者的工作。