Mybatis学习(一)—— Mybatis的简介及HelloWorld程序
MyBatis的介绍
MyBatis原本是Apache下的一个项目,但是在MyBatis2.0 及 1.0版本的时候,不叫MyBatis,而是叫做IBatis,所以不管是MyBatis还是IBatis,实际上是一回事。是因为在IBatis3.0版本即将推出的时候,这个团队整体的转战到了谷歌旗下,3.0之后就叫做MyBatis了,MyBatis是一个非常优秀的持久化层的半自动框架,和之前接触的JDBC,DBUtil,JdbcTemplate一样,底层都是JDBC原生的技术,是Java连接数据库的标准,DBUtil,JdbcTemplate充其量是一个小工具,不能称之为框架,框架一定是一个整体的解决方案。
原生JDBC的开发流程
操作步骤
- 加载驱动程序 Class.forName(“驱动类全类名”)
- 取得数据库连接 Connection conn = DriverManager.getConnection(url,username,password)
- 编写sql语句
- 执行sql,操作数据库(Statement / PreparedStatement / ResultSet)
- 如果是增删改,并且执行多条sql操作,需要进行事务管理
- 关闭数据库
- 释放资源
JDBC的缺点
- 代码冗余:相似的代码到处都是
- 功能单一:不能完成缓存操作,不能自动的封装结果集
- 代码耦合度高:所有的sql语句都是写在Java程序中,耦合度高,维护不便
这个时候我们不得不使用工具或者框架,但是工具同样存在以上的问题,所以我们必须使用框架完成持久化层开发
目前比较流行的框架有
- Hibernate:是一个非常优秀的全自动框架,Hibernate旨在消除sql,意味着一个不懂sql的人,也可以使用Hiberate进行数据访问层开发,在开发过程中,完全针对的是实体类的对象,比如创建一个对象,save(对象),这个对象就通过ORM映射关系保存到表中,但是Hibernate存在问题是
① 不需要编写sql,所以一些复杂的查询由Hibernate自动生成sql,不能进行优化,
② 对于复杂查询来说我们还需要编写另外的一种语句叫做HQL,所以学习成本增加,
③ Hibernate不许程序员编写sql,而是由Hibernate自动的生成sql,所以执行效率比较低。 - JAP+SpringData:和Hibernate几乎一样。
- MyBaits:是一个非常优秀的半自动框架,不像Hibernate那样进行黑箱操作,而是将sql的编写交个程序员,而且sql也不用编写在Java程序中,而是使用xml配置方式编写sql语句,所以Mybais的特点:
① sql与Java代码相分离,
② SQL语句是程序员编写,可以进行更好的优化,
③ 除了已经学过的sql之前,不会像Hibernate那样学其他的QL语句。
Mybatis HelloWorld
步骤如下:
①:创建Java工程:MyBatis01
②:加入jar包
mybatis-3.4.1.jar
mysql-connector-java-5.1.37-bin.jar
③:使用ssm数据库中的emp表
④:创建对应的实体类
⑤:为了更加方便的输出MyBatis的日志,我们加入log4j配置和jar包
log4j.xml和log4j.jar
⑥:现在基础环境已经搭建好了,接下来就是MyBatis的配置
⑦:创建MyBatis的全局配置文件
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///ssm" />
<property name="username" value="root" />
<property name="password" value="a123456" />
</dataSource>
</environment>
</environments>
<!-- 注册sql映射文件 -->
<mappers>
<mapper resource="com/zym/dao/EmpDao.xml" />
</mappers>
</configuration>
⑧:创建Emp表的sql映射文件,同时将SQL映射文件注册到全局配置文件中
EmpDao.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">
<mapper namespace="com.wanbangee.dao.EmpDao">
<select id="selectEmp" resultType="com.zym.entities.Emp">
select * from emp
</select>
</mapper>
⑨:测试
@Test
public void testEmpQuery() throws Exception {
//获取全局配置文件文件的字节输入流
InputStream input = Resources.getResourceAsStream("mybatis-conf.xml");
//创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
//获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用dao
List<Emp> emps = sqlSession.selectList("com.zym.dao.EmpDao.selectEmp", null);
System.out.println(emps);
sqlSession.close();
}
上面测试的查询结果集的数据都为null,这是什么原因呢?因为MyBatis3.4.1 默认的自动驼峰没有开启,需要我们手动开启。在MyBatis全局配置文件中添加。
<configuration>
<settings>
<!-- 设置自动驼峰命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
</settings>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///ssm" />
<property name="username" value="root" />
<property name="password" value="a123456" />
</dataSource>
</environment>
</environments>
<!-- 注册sql映射文件 -->
<mappers>
<mapper resource="com/zym/dao/EmpDao.xml" />
</mappers>
</configuration>
MyBatis中的接口式编程(开发中最常用的)
在原始的MVC开发中,我们编写DAO层时,使用dao接口+dao实现类的形式,例如,一般都在com.xxx.dao包下写IEmpDao的接口,在com.xxx.dao。impl包下写IEmpDao的接口的实现类EmpDaoImpl,但在MyBatis中,可以不用编写实现类,直接写dao接口,该接口的实现会由MyBatis使用动态代理的形式实现
测试案例
编写根据ID查询Emp的sql语句及测试:
<select id="selectEmpByEmpId" resultType="com.wanbangee.entities.Emp">
select * from emp where emp_id = #{id}
</select>
@Test
public void testSelectEmpByEmpId() throws Exception {
//获取全局配置文件文件的字节输入流
InputStream input = Resources.getResourceAsStream("mybatis-conf.xml");
//创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
//获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
Emp emp = sqlSession.selectOne("com.zym.dao.EmpDao.selectEmpByEmpId", 1);
System.out.println(emp);
sqlSession.close();
}
以上的程序,会不会出现问题呢?当然会有问题,我们没有办法指定传入sql的参数类型,可以随意传递任意类型,但是结果却差强人意。而且在开中也存在问题,所谓三层架构,Service层如何调用sql呢?
所以我们要使用接口式编程:
- 定义数据访问层接口
- 将数据访问层接口和sql映射文件相绑定
- 将接口的每个方法和sql映射文件中的每一条sql语句相绑定
①:定义接口
public interface EmpDao {
public List<Emp> selectEmp();
public Emp selectEmpByEmpId(Integer empId);
public void insertEmp(Emp emp);
public void deleteEmp(Integer empId);
}
②:定义此接口的sql映射文件(实现简单的CRUD)
<?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">
<!--
mapper : 表示此xml是sql映射文件
namespace : 命名空间,表示此sql映射文件是和那个DAO接口对应的
-->
<mapper namespace="com.zym.dao.EmpDao">
<!-- public List<Emp> selectEmp();
-->
<select id="selectEmp" resultType="com.zym.entities.Emp">
select * from emp
</select>
<!--
public Emp selectEmpByEmpId(Integer empId);
select : 表示此方法进行的查询操作
id : sql的ID,必须和对应接口中的一个方法名绑定,表示此条sql就是这个方法的sql语句
resultType : 表示的是方法的返回值类型(全类名),如果返回值类型是List,那么只需要编写List中存放对象的全类名即可
parameterType : 参数类型,一般情况下,此属性可以省略不写,表示指定传入参数的类型
sql中的#{} 是表示sql以预处理的形式,使用?作为占位符执行sql,
如果传入的参数是一个普通的参数【基本数据类型,及包装类对象,或者字符串】,那么#{内容}
-->
<select id="selectEmpByEmpId" resultType="com.zym.entities.Emp">
select * from emp where emp_id = #{empId}
</select>
<!--
public void insertEmp(Emp emp);
如果传入的参数为实体类的对象,那么在sql中,必须使用#{实体类JavaBean风格的属性名} 进行参数绑定
-->
<insert id = "insertEmp">
insert into emp(emp_name,emp_mail,emp_gender,dept_id) values(#{empName},#{empMail},#{empGender},#{deptId})
</insert>
<!--
public void deleteEmp(Integer empId)
-->
<delete id="deleteEmp">
delete from emp where emp_id = #{empId}
</delete>
</mapper>
③:测试
public void testEmpQuery() throws Exception {
// 获取全局配置文件文件的字节输入流
InputStream input = Resources.getResourceAsStream("mybatis-conf.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// List<Emp> emps = sqlSession.selectList("com.wanbangee.dao.EmpDao.selectEmp",
// null);//非接口式编程
// 接口式编程
// ① 获取接口的实现类对象(通过动态代理完成)
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// ② 调用接口方法
List<Emp> emps = empDao.selectEmp();// 调用接口方法
System.out.println(emps);
sqlSession.close();
}
@Test
public void testSelectEmpByEmpId() throws Exception {
// 获取全局配置文件文件的字节输入流
InputStream input = Resources.getResourceAsStream("mybatis-conf.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// Emp emp = sqlSession.selectOne("com.wanbangee.dao.EmpDao.selectEmpByEmpId", 1);
// 接口式编程
// ① 获取接口的实现类对象(通过动态代理完成)
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// ② 调用接口方法
Emp emp = empDao.selectEmpByEmpId(1);
System.out.println(emp);
sqlSession.close();
}
@Test
public void testInsertEmp() throws Exception {
// 获取全局配置文件文件的字节输入流
InputStream input = Resources.getResourceAsStream("mybatis-conf.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// Emp emp = sqlSession.selectOne("com.wanbangee.dao.EmpDao.selectEmpByEmpId", 1);
// 接口式编程
// ① 获取接口的实现类对象(通过动态代理完成)
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// ② 调用接口方法
Emp emp = new Emp();
emp.setEmpName("AABB");
emp.setEmpMail("aabb@163.com");
empDao.insertEmp(emp);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testDeleteEmp() throws Exception {
// 获取全局配置文件文件的字节输入流
InputStream input = Resources.getResourceAsStream("mybatis-conf.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
// 获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// Emp emp = sqlSession.selectOne("com.wanbangee.dao.EmpDao.selectEmpByEmpId", 1);
// 接口式编程
// ① 获取接口的实现类对象(通过动态代理完成)
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// ② 调用接口方法
empDao.deleteEmp(11);
sqlSession.commit();
sqlSession.close();
}
以上就是接口式编程,在开发中,实际上也是全部使用接口式编程完成,有很多优点:
- 解耦合,java代码和sql已经分离了,而且数据访问层有实际的接口存在,整合Spring的时候,就可以将数据访问层加入到IOC容器中了
- 入参为指定类型,调用方法的时候,参数的类型不能任意了,必须符合接口定义的方法的入参的标准
总结
- 接口式编程
原生:DAO----》DAOImpl
Mybatis:Dao—》Dao.xml - SqlSession表示的是和数据库的一次会话,所以用完必须关闭
- SqlSession和Connection一样,都是非线程安全的,所以每次的操作必须重新的获取对象。
- 一张表 ----- 一个 实体类 ----- 一个Dao.java ----- 一个Dao.xml
- Dao接口是没有实现类的,但是Mybatis会给这个接口生成一个代理对象(需要将接口和映射文件进行绑定)
- 两个重要的配置文件:
· Mybatis的全局配置文件:包含了数据库的连接信息,也可以包含事务管理,系统的运行环境等等
· sql映射文件:保存了每一条sql语句的映射信息,这个文件就是将sql抽取出来的保存的文件。