mybatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。 目前mybatis在github上托管。git(分布式版本控制,当前比较流程)
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
mybatis架构
mybatis入门程序(用户的增删改查)
User.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace命名空间,为了对sql语句进行隔离,方便管理 ,mapper开发dao方式,使用namespace有特殊作用 --> <mapper namespace="test"> <!-- 在mapper.xml文件中配置很多的sql语句,执行每个sql语句时,封装为MappedStatement对象 mapper.xml以statement为单位管理sql语句 --> <!-- 根据id查询用户信息 --> <!-- id:唯一标识 一个statement #{}:表示 一个占位符,如果#{}中传入简单类型的参数,#{}中的名称随意 parameterType:输入 参数的类型,通过#{}接收parameterType输入 的参数 resultType:输出结果 类型,不管返回是多条还是单条,指定单条记录映射的pojo类型 --> <select id="findUserById" parameterType="int" resultType="cn.yzu.mybatis.po.User"> SELECT * FROM USER WHERE id= #{id} </select> <!-- 根据用户名称查询用户信息,可能返回多条 ${}:表示sql的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在sql中。 --> <select id="findUserByName" parameterType="java.lang.String" resultType="cn.yzu.mybatis.po.User"> select * from user where username like '%${value}%' </select> <!-- 添加用户 parameterType:输入 参数的类型,User对象 包括 username,birthday,sex,address #{}接收pojo数据,可以使用OGNL解析出pojo的属性值 #{username}表示从parameterType中获取pojo的属性值 selectKey:用于进行主键返回,定义了获取主键值的sql order:设置selectKey中sql执行的顺序,相对于insert语句来说 keyProperty:将主键值设置到哪个属性 resultType:select LAST_INSERT_ID()的结果 类型 --> <insert id="insertUser" parameterType="cn.yzu.mybatis.po.User"> <selectKey keyProperty="id" order="AFTER" resultType="int"> select LAST_INSERT_ID() </selectKey> INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address}) </insert> <!-- 用户删除 --> <delete id="deleteUser" parameterType="int"> delete from user where id=#{id} </delete> <!-- 用户更新 要求:传入的user对象中包括 id属性值 --> <update id="updateUser" parameterType="cn.yzu.mybatis.po.User"> update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update> </mapper>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC" /> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="123456" /> </dataSource> </environment> </environments> <!-- 加载mapper.xml --> <mappers> <mapper resource="sqlmap/User.xml" /> </mappers> </configuration>
public class User { private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address=" + address + "]"; } }
public class MybatisFirst { // 会话工厂 private SqlSessionFactory sqlSessionFactory; // 创建工厂 @Before public void init() throws IOException { // 配置文件(SqlMapConfig.xml) String resource = "SqlMapConfig.xml"; // 加载配置文件到输入 流 InputStream inputStream = Resources.getResourceAsStream(resource); // 创建会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } // 测试根据id查询用户(得到单条记录) @Test public void testFindUserById() { // 通过sqlSessionFactory创建sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 通过sqlSession操作数据库 // 第一个参数:statement的位置,等于namespace+statement的id // 第二个参数:传入的参数 User user = null; try { user = sqlSession.selectOne("test.findUserById", 16); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭sqlSession sqlSession.close(); } System.out.println(user); } // 测试根据id查询用户(得到单条记录) @Test public void testFindUserByName() { // 通过sqlSessionFactory创建sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 通过sqlSession操作数据库 // 第一个参数:statement的位置,等于namespace+statement的id // 第二个参数:传入的参数 List<User> list = null; try { list = sqlSession.selectList("test.findUserByName", "小明"); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } System.out.println(list.get(0).getUsername()); } // 测试根据id查询用户(得到单条记录) @Test public void testInsertUser() { // 通过sqlSessionFactory创建sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 通过sqlSession操作数据库 // 创建插入数据对象 User user = new User(); user.setUsername("浪子燕青2"); user.setAddress("河南郑州"); user.setBirthday(new Date()); user.setSex("1"); try { sqlSession.insert("test.insertUser", user); // 需要提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } System.out.println("用户的id=" + user.getId()); } // 测试根据id删除用户(得到单条记录) @Test public void testDeleteUser() { // 通过sqlSessionFactory创建sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 通过sqlSession操作数据库 try { sqlSession.delete("test.deleteUser", 29); sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } } // 测试根据id更新用户(得到单条记录) @Test public void testUpdateUser() { // 通过sqlSessionFactory创建sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 通过sqlSession操作数据库 // 创建更新数据对象,要求必须包括 id User user = new User(); user.setId(28); user.setUsername("燕青"); user.setAddress("河南郑州"); //user.setBirthday(new Date()); user.setSex("1"); try { sqlSession.update("test.updateUser", user); // 需要提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } System.out.println("用户的id=" + user.getId()); } }
Mybatis解决jdbc编程的问题
数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
mybatis与hibernate重要区别
企业开发进行技术选型 ,考虑mybatis与hibernate适用场景。
mybatis:入门简单,程序容易上手开发,节省开发成本 。mybatis需要程序员自己编写sql语句,是一个不完全 的ORM框架,对sql修改和优化非常容易实现 。
mybatis适合开发需求变更频繁的系统,比如:互联网项目。
hibernate:入门门槛高,如果用hibernate写出高性能的程序不容易实现。hibernate不用写sql语句,是一个 ORM框架。
hibernate适合需求固定,对象数据模型稳定,中小型项目,比如:企业OA系统。
总之,企业在技术选型时根据项目实际情况,以降低成本和提高系统 可维护性为出发点进行技术选型。
总结
SqlMapConfig.xml
是mybatis全局配置文件,只有一个,名称不固定的,主要mapper.xml,mapper.xml中配置 sql语句
mapper.xml
mapper.xml是以statement为单位进行配置。(把一个sql称为一个statement),satatement中配置 sql语句、parameterType输入参数类型(完成输入映射)、resultType输出结果类型(完成输出映射)。还提供了parameterMap配置输入参数类型(过期了,不推荐使用了),还提供resultMap配置输出结果类型(完成输出映射)
#{}
表示一个占位符,向占位符输入参数,mybatis自动进行java类型和jdbc类型的转换。程序员不需要考虑参数的类型,比如:传入字符串,mybatis最终拼接好的sql就是参数两边加单引号。#{}接收pojo数据,可以使用OGNL解析出pojo的属性值
${}
表示sql的拼接,通过${}接收参数,将参数的内容不加任何修饰拼接在sql中。${}也可以接收pojo数据,可以使用OGNL解析出pojo的属性值
缺点:不能防止sql注入。
selectOne
用于查询单条记录,不能用于查询多条记录,否则异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4
selectList
用于查询多条记录,可以用于查询单条记录的。