1、 什么叫ibatis
⑴、概念(官方文档)
MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(PlanOld Java Objects,普通的Java对象)映射成数据库中的记录。
总结:dao层,操作数据库,提供对sql的封装,作用跟jdbc,hibernate类似。
2.0版本:ibatis 网址:http://ibatis.apache.org/
3.0版本:mybatis 网址:http://www.mybatis.org/
时间分隔点:2010/06/16
从3.0之后,移往google code :http://code.google.com/p/mybatis/wiki/Welcome
⑵、特点
相对Hibernate和Apache OJB 等“一站式”ORM解决方案而言,ibatis 是一种“半自动化”的ORM实现。 纵观目前主流的ORM,无论Hibernate 还是Apache OJB,都对数据库结构提供了较为完整的封装,提供了从POJO 到数据库表的全套映射机制。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate 或者OJB 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握,Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执 行。 ---- 摘自官方资料的一段话
总结:ibatis是一种半自动化的ORM, 需要手工编写sql ;hibernate不需要手工编写sql。
2、 优点
⑴、iBATIS被广泛认为是最简单的一种持久化框架。
sql可以写在xml中,结构清晰,灵活配置。
⑵、文件归类,select 后的字须定义到标签中,可以实现部分代码复用。
⑶、执行sql后,返回的结果集自动封装。类似以下代码,均可省略。
while (rs.next()) {
so = new SoQueryMVO();
so.setQryFlag(rs.getString("qryFlag"));// so.setSoNbr(rs.getString("SO_NBR"));
so.setExtSoNbr(rs.getString("EXT_SO_NBR"));
so.setCoNbr(rs.getString("CO_NBR"));
so.setSoStaffName(rs.getString("SO_STAFF_NAME"));
so.setSoWorkAreaName(rs.getString("SO_WORK_AREA_NAME"));
so.setApplDate(rs.getTimestamp("APPL_DATE"));
so.setContactResult(rs.getString("CONTACT_RESULT"));
so.setBillType(rs.getString("BILL_TYPE"));
so.setPaySts(rs.getString("PAY_STS"));
res.add(so);
}
⑷、MyBatis真正的力量是在映射语句中。这里是奇迹发生的地方。对于所有的力量,SQL映射的XML文件是相当的简单。当然如果你将它们和对等功能的JDBC代码来比较,你会发现映射文件节省了大约95%的代码量。MyBatis的构建就是聚焦于SQL的,使其远离于普通的方式。
简洁的代码和简单的配置使得使用iBATIS所需的代码量可以减少到了相应JDBC代码的62%
总结一下:可以省代码。
⑸、动态sql, 没觉得有多好。
⑹、iBATIS改进了应用的设计方式以确保未来的可维护性,后期可维护性增加。
⑺、基于xml的,所以适合多平台。
iBATIS可以用任何具有完备功能的编程语言来实现。
3、 缺点
⑴、开源的东西,文档资料少,官方说明文档太简单,真正有应用价值的信息不多。
以官方文档为例:60%左右的代码讲述环境配置,即相当于我们的connection-config.xml配置,及创建一个链接, 对于服务开通来说,使用的是java代码,连接oracle库,一旦配置好环境,对于开发人员来说,专业于某些功能点,业务逻辑、流程、配置等的实现,在以后的开发过程中,其实很少关注环境。
这部分功能实现可以放到工具类中,对我们来说,文章中的说明没有什么大的使用价值。
⑵、实现了结果集自动封装,一把双刃剑。
①、返回的结果集封装结果单一,不能实现灵活封装。如Map<key=id ,value =vo >
②、不支持嵌套vo对象,如果有同名字段,会覆盖。
关联查询,需要返回很多信息,很有可能需要复写MVO,把嵌套的vo,拿出来,放到第一层的vo里面。
因为mvo改变,原来jsp页面上使用soMVO.soResMVO.resId,诸如这种形势,有可能修改前台页面。
③、以查询为例,入参单一,只有一个入参,如果有两个以上的参数,必须定义到一个对象中,感觉不灵活。
当然可以用paraMap方式进行配置,麻烦,感觉还不如定义到一个对象中。
④、部分写在dao中的逻辑,改动起来麻烦。目前的目标是只修改domIpml类,其它的尽量不修改,现在看来,
如此肯定不行了。部分复杂的逻辑靠标签无法实现。
if (!StringUtil.isBlank(so.getChbAccNbr())&& !StringUtil.isBlank(so.getAccNbr())) {
// 增加批量查询业务号码
isAvaibleCondition = true;
String tempAccNbr[] = so.getAccNbr().split(";");
List accNbrList = (List) CollectionFactory
.createCollection(CollectionFactory.COLLECTION_LIST);
for (int i = 0; i < tempAccNbr.length; i++) {
accNbrList.add(tempAccNbr[i].trim());
}
if (accNbrList.size() == 1) {
sql.append(" AND SO.SO_NBRIN ( ");
sql.append(" SELECTDISTINCT SO_NBR FROM SO_ACC_NBR where ");
if(!StringUtil.isBlank(so.getLocalNetId())) {
sql.append(" local_net_id=:lcId ");
sql.setLong("lcId", so.getLocalNetId());
sql.append(" andSTS = 'A' AND ACC_NBR LIKE :accNbr )");
sql.setString("accNbr", so.getAccNbr().trim() +"%");
} else {
sql.append(" STS ='A' AND ACC_NBR LIKE :accNbr )");
sql.setString("accNbr", so.getAccNbr().trim() +"%");
}
} else {
sql.append(" AND SO.SO_NBRIN ( ");
sql.append(" SELECTDISTINCT SO_NBR FROM SO_ACC_NBR where ");
if(!StringUtil.isBlank(so.getLocalNetId())) {
sql.append(" local_net_id=:lcId AND STS = 'A' ");
sql.setLong("lcId", so.getLocalNetId());
} else {
sql.append(" STS ='A' ");
}
int i = 0;
while (i <accNbrList.size()) {
if (i == 0)
sql.append(" AND (ACC_NBR = :accNbr");
else
sql.append(" OR ACC_NBR = :accNbr");
sql.append(""+ i + "");
sql.setString("accNbr" + i + " ", accNbrList.get(i)+ "");
i++;
}
sql.append(" ))");
}
}
⑤、其它
⑶、sql写在xml, 没有办法打断点,调试非常不方便,及其浪费时间。
有可以十几分钟写的一个大sql,完事后,调试了一天还是编译性错误,完全不夸张,很多可能。
⑷、日志输出不完全,可能是我还没有找到方法。
无法输出预编译赋值之后的sql ,查看不方便 (原来咱们系统中使用Sql.log实现)
⑸、错误信息提示异样 ,报错信息比较混乱,有些时间没有参考价值,没有起到提示作用。
⑹、文档少,现有的文档提供的标签,也不能完全满足我们的需求
如有if 没有else,形式上感到别扭,某些
大小写是否敏感?
Ibatis适用于小项目,简单的业务逻辑映射,尤其是单表操作,很方便。
下面我们来看看在Spring Mvc + Maven中如何配置iBatis。
这边给出项目的目录结构
1.在pom.xml中写入ibatis包的依赖
<dependency> <groupId>com.ibatis</groupId> <artifactId>ibatis2-sqlmap</artifactId> <version>2.1.7.597</version> </dependency>
2.配置SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <!-- 引用JDBC属性的配置文件 --> <properties resource="datasource.properties" /> <!-- 使用JDBC的事务管理 --> <transactionManager type="JDBC"> <!-- 数据源 --> <dataSource type="SIMPLE"> <property name="JDBC.Driver" value="${db.driverClassName}" /> <property name="JDBC.ConnectionURL" value="${db.jdbcUrl}" /> <property name="JDBC.Username" value="${db.user}" /> <property name="JDBC.Password" value="${db.password}" /> </dataSource> </transactionManager> <!-- 这里可以写多个实体的映射文件 --> <sqlMap resource="com/jeader/tad/User.xml" /> </sqlMapConfig>
注意:properties文件的路径也要在resources下
<properties resource="datasource.properties" />
3.datasource.properties
db.driverClassName=com.mysql.jdbc.Driver db.jdbcUrl=jdbc:mysql://localhost:3306/taddb?useUnicode=true&characterEncoding=utf-8 db.user=taduser db.password=test
4.User.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap> <!-- 通过typeAlias使得我们在下面使用User实体类的时候不需要写包名 --> <typeAlias alias="User" type="com.jeader.tad.demo.User" /> <!-- 这样以后改了sql,就不需要去改java代码了 --> <!-- id表示select里的sql语句,resultClass表示返回结果的类型 --> <select id="selectAllUser" resultClass="User"> select * from user </select> <!-- parameterClass表示参数的内容 --> <!-- #表示这是一个外部调用的需要传进的参数,可以理解为占位符 --> <select id="selectUserById" parameterClass="int" resultClass="User"> select * from User where id=#id# </select> <!-- 注意这里的resultClass类型,使用User类型取决于queryForList还是queryForObject --> <select id="selectUserByName" parameterClass="string" resultClass="User"> select name,birthday from User where name like '%$name$%' </select> <insert id="addUser" parameterClass="User"> insert into User(ID,USER_NAME,NAME,PASSWORD,SEX,IDENTITY_CARD_NO,BIRTHDAY,UPDATE_DATE) values (#id#,#user_name#,#name#,#password#,#sex#,#identity_card_no#,#birthday#,#update_date#) <selectKey resultClass="int" keyProperty="id"> select @@identity as inserted <!-- 这里需要说明一下不同的数据库主键的生成,对各自的数据库有不同的方式: --> <!-- mysql:SELECT LAST_INSERT_ID() AS VALUE --> <!-- mssql:select @@IDENTITY as value --> <!-- oracle:SELECT STOCKIDSEQUENCE.NEXTVAL AS VALUE FROM DUAL --> <!-- 还有一点需要注意的是不同的数据库生产商生成主键的方式不一样,有些是预先生成 (pre-generate)主键的,如Oracle和PostgreSQL。 有些是事后生成(post-generate)主键的,如MySQL和SQL Server 所以如果是Oracle数据库,则需要将selectKey写在insert之前 --> </selectKey> </insert> <delete id="deleteUserById" parameterClass="int"> <!-- #id#里的id可以随意取,但是上面的insert则会有影响,因为上面的name会从User里的属性里去查找 --> <!-- 我们也可以这样理解,如果有#占位符,则ibatis会调用parameterClass里的属性去赋值 --> delete from User where id=#id# </delete> <update id="updateUser" parameterClass="User"> update User set name=#name# where id=#id# </update> </sqlMap>
5.UserDao.java
package com.jeader.tad.demo; import java.util.List; public interface UserDao { /** * 添加学生信息 * * @param user * 学生实体 * @return 返回是否添加成功 */ public boolean addUser(User user); /** * 根据学生id删除学生信息 * * @param id * 学生id * @return 删除是否成功 */ public boolean deleteUserById(int id); /** * 更新学生信息 * * @param user * 学生实体 * @return 更新是否成功 */ public boolean updateUser(User user); /** * 查询全部学生信息 * * @return 返回学生列表 */ public List<User> selectAllUser(); /** * 根据学生姓名模糊查询学生信息 * * @param name * 学生姓名 * @return 学生信息列表 */ public List<User> selectUserByName(String name); /** * 根据学生id查询学生信息 * * @param id * 学生id * @return 学生对象 */ public User selectUserById(int id); }
6.UserDaoImpl.java
package com.jeader.tad.demo; import java.io.IOException; import java.io.Reader; import java.sql.SQLException; import java.util.List; import com.ibatis.common.resources.Resources; import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.client.SqlMapClientBuilder; public class UserDaoImpl implements UserDao { private static SqlMapClient sqlMapClient = null; // 读取配置文件 static { try { Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader); reader.close(); } catch (IOException e) { e.printStackTrace(); } } public boolean addUser(User user) { Object object = null; boolean flag = false; try { object = sqlMapClient.insert("addUser", user); System.out.println("添加学生信息的返回值:" + object); } catch (SQLException e) { e.printStackTrace(); } if (object != null) { flag = true; } return flag; } public boolean deleteUserById(int id) { boolean flag = false; Object object = null; try { object = sqlMapClient.delete("deleteUserById", id); System.out.println("删除学生信息的返回值:" + object + ",这里返回的是影响的行数"); } catch (SQLException e) { e.printStackTrace(); } if (object != null) { flag = true; } return flag; } public boolean updateUser(User user) { boolean flag = false; Object object = false; try { object = sqlMapClient.update("updateUser", user); System.out.println("更新学生信息的返回值:" + object + ",返回影响的行数"); } catch (SQLException e) { e.printStackTrace(); } if (object != null) { flag = true; } return flag; } public List<User> selectAllUser() { List<User> users = null; try { users = sqlMapClient.queryForList("selectAllUser", new Object()); } catch (SQLException e) { e.printStackTrace(); } return users; } public List<User> selectUserByName(String name) { List<User> users = null; try { users = sqlMapClient.queryForList("selectUserByName", name); } catch (SQLException e) { e.printStackTrace(); } return users; } public User selectUserById(int id) { User user = null; try { user = (User) sqlMapClient.queryForObject("selectUserById", id); } catch (SQLException e) { e.printStackTrace(); } return user; } }
注意:SqlMapConfig.xml文件一定要在resources下面,不然下面的语句就会报错。
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
7.User.java
package com.jeader.tad.demo; import java.sql.Timestamp; public class User { private int id; private String user_name; private String name; private String password; private int sex; private String identity_card_no; private String birthday; private Timestamp update_date; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUser_name() { return user_name; } public void setUser_name(String user_name) { this.user_name = user_name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getIdentity_card_no() { return identity_card_no; } public void setIdentity_card_no(String identity_card_no) { this.identity_card_no = identity_card_no; } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday; } public Timestamp getUpdate_date() { return update_date; } public void setUpdate_date(Timestamp update_date) { this.update_date = update_date; } @Override public String toString() { return "id=" + id + ", user_name=" + user_name + ", name=" + name + ", password=" + password + ", sex" + sex + ", identity_card_no" + identity_card_no + ", birthday" + birthday + ", update_date" + update_date + "\n"; } }
8.测试类TestIbatis.java
package com.jeader.tad.demo; import java.sql.Timestamp; import java.util.List; public class TestIbatis { public static void main(String[] args) { UserDaoImpl userDaoImpl = new UserDaoImpl(); System.out.println("测试插入"); User addUser = new User(); addUser.setId(2); addUser.setUser_name("李四"); addUser.setName("lisi"); addUser.setPassword("test"); addUser.setSex(1); addUser.setIdentity_card_no("320922198702221014"); addUser.setBirthday("19870101"); addUser.setUpdate_date(Timestamp.valueOf("2015-09-11 12:00:00")); System.out.println(userDaoImpl.addUser(addUser)); System.out.println("测试根据id查询"); userDaoImpl.selectUserById(1); System.out.println("测试模糊查询"); List<User> mohuLists = userDaoImpl.selectUserByName("李"); for (User user : mohuLists) { System.out.println(user); } System.out.println("测试查询所有"); List<User> users = userDaoImpl.selectAllUser(); for (User student : users) { System.out.println(student); } System.out.println("根据id删除学生信息"); System.out.println(userDaoImpl.deleteUserById(2)); System.out.println("测试更新学生信息"); User updateUser = new User(); updateUser.setId(1); updateUser.setName("李四1"); System.out.println(userDaoImpl.updateUser(updateUser)); } }
9.运行结果
测试插入 添加学生信息的返回值:0 true 测试根据id查询 测试模糊查询 id=0, user_name=null, name=李四1, password=null, sex0, identity_card_nonull, birthday19870101, update_datenull 测试查询所有 id=1, user_name=test, name=李四1, password=test, sex1, identity_card_no320922198702022325, birthday19870101, update_date2015-11-08 16:36:31.0 id=2, user_name=李四, name=lisi, password=test, sex1, identity_card_no320922198702221014, birthday19870101, update_date2015-09-11 12:00:00.0 根据id删除学生信息 删除学生信息的返回值:1,这里返回的是影响的行数 true 测试更新学生信息 更新学生信息的返回值:1,返回影响的行数 true