1. Mybatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
1.1. jdbc编程步骤:
1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建jdbc statement对象
4、 设置sql语句
5、 设置sql语句中的参数(使用preparedStatement)
6、 通过statement执行sql并获取结果
7、 对sql执行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)
1.1. jdbc问题总结如下:
1、 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
2、 Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。
3、 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
4、 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
1. Mybatis架构
1、 mybatis配置
SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。执行者
5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
实验
创建一个项目
创建lib文件夹,然后将jar包粘贴过去,然后build path
加入配置文件
配置文件按照上面的mybatis需要多个,第一个是总的配置文件sqlMapConfig.xml,这个配置文件配置之后的Mapper.xml,我们一般有一个数据库表,我们就创建一个Mapper.xml,这个可以帮助我们配置这个数据库相关的数据库操作,比如针对于User数据库,我们需要设置一个User.xml来专门帮助我们配置user表的相关操作:
<?xml version="1.0" encoding="UTF-8" ?>-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">select * from user where id = #{value}select * from user where username like "%"#{value}"%"select LAST_INSERT_ID()insert into user (username,birthday,address,sex) values (#{username},#{birthday},#{address},#{sex})update user set username = #{username},sex = #{sex},birthday = #{birthday},address = #{address}where id = #{id}delete from user where id = #{value}
我们可以看到首先,我们通过namespace来定义一个命名空间,然后每条sql语句都有id,之后就可以通过命名空间.id来表示详细的调用的sql语句了。
然后就写sql语句,sql语句就有四种,增删改查,查是select,删是delete,改是update,增insert
然后每个语句需要写上id,这个前面说过 ,然后还要写上ParameterType表示传递的参数类型,resultType表示查询结果的类型,所以select需要写上查询的类型,注意计算是查询结果返回是列表,这里的类型指的是列表中的每一个元素的类型,也就是泛型。
mybatis的强大之处在于它可以将将查询结果进行封装,这个关键点在于javaBean应该和数据库的字段名是一致的
#{}表示从传递过来的参数中取值,当参数只有一个的时候,此时怎么写都行,不一定要写value,但是要是传递一个对象的话,那么就得#{对象的属性了},
其中在插入语句中还有如下所示的配置
select LAST_INSERT_ID()
因为插入一个数据的时候,我们并不需要id,因为id是自增长的,所以不需要,但是我们想插入之后,获取到新插入用户的id,那么此时我们可以通过这种方式来获取到id,并且返回,其中keyPropeerty表示我们想要获取的属性,resultType表示类型,order表示执行顺序,肯定是先插入在获取id了,这个是不会有问题的。
配置好这个之后就可以调用这些sql语句了
<?xml version="1.0" encoding="UTF-8" ?>-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<?xml version="1.0" encoding="UTF-8" ?>-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">select * from user where id = #{value}select * from user where username like "%"#{value}"%"select LAST_INSERT_ID()insert into user (username,birthday,address,sex) values (#{username},#{birthday},#{address},#{sex})update user set username = #{username},sex = #{sex},birthday = #{birthday},address = #{address}where id = #{id}delete from user where id = #{value}
配置日志文件/mybatis/src/log4j.properties
# Global logging configurationlog4j.rootLogger=DEBUG, stdout# Console output...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
测试
package com.huanfeng.test;import java.io.IOException;import java.io.InputStream;import java.util.Date;import java.util.List;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Test;import com.huanfeng.bean.User;public class Demo {@Testpublic void testMybatis() throws Exception {//加载核心配置文件String resource = "sqlMapConfig.xml";InputStream in = Resources.getResourceAsStream(resource);//创建SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//执行Sql语句 User user = sqlSession.selectOne("test.findUserById", 10);System.out.println(user);}//根据用户名称模糊查询用户列表@Testpublic void testfindUserByUsername() throws Exception {//加载核心配置文件String resource = "sqlMapConfig.xml";InputStream in = Resources.getResourceAsStream(resource);//创建SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//执行Sql语句 List users = sqlSession.selectList("test.findUserByUsername", "五");for (User user2 : users) {System.out.println(user2);}}//添加用户@Testpublic void testInsertUser() throws Exception {//加载核心配置文件String resource = "sqlMapConfig.xml";InputStream in = Resources.getResourceAsStream(resource);//创建SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//执行Sql语句 User user = new User();user.setUsername("huanfeng");user.setBirthday(new Date());user.setAddress("aia");user.setSex("男");int i = sqlSession.insert("test.insertUser", user);sqlSession.commit();System.out.println(user.getId());}//更新用户@Testpublic void testUpdateUserById() throws Exception {//加载核心配置文件String resource = "sqlMapConfig.xml";InputStream in = Resources.getResourceAsStream(resource);//创建SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//执行Sql语句 User user = new User();user.setId(27);user.setUsername("huanfeng9");user.setBirthday(new Date());user.setAddress("as");user.setSex("男");int i = sqlSession.update("test.updateUserById", user);sqlSession.commit();}//删除@Testpublic void testDelete() throws Exception {//加载核心配置文件String resource = "sqlMapConfig.xml";InputStream in = Resources.getResourceAsStream(resource);//创建SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();sqlSession.delete("test.deleteUserById", 27);sqlSession.commit();}}
现在的任务
现在的任务就是如何将sqlSession配置好,然后在dao中直接使用它:其中sqlSession是由sqlSessionFactory工厂创建的,所以每次执行sql语句都应该创建一个sqlSession,也就是说我们可以公用一个工厂,但是需要有多个sqlSession
Mapper动态代理开发
真正写的时候,我们需要在service层获取到sqlSessionFactory,也就是只创建一个,然后传递到Dao层,Dao层只获取到工厂之后就创建sqlSession,然后就可以通过SqlSession进行sql执行了,其实我们还可以进一步的变化,我们可以看到在Dao层中每次都要通过SqlSesssionFactory来获取SqlSession,然后执行对应的增删改查方法,很费事,要想解决这个问题可以使用Mapper动态代理的开发模式。
具体来说建立一个接口,这个结构中的抽象方法需要满足以下几个条件,只要满足以下几个条件,mybatis就可以自动帮助我们创建如下所示的代码了:
SqlSession sqlSession = sqlSessionFactory.openSession();//执行Sql语句 User user = sqlSession.selectOne("test.findUserById", 10);System.out.println(user);
我们可以在接口中进行如下方式的操作:
public interface UserMapper {public User findUserById(Integer id);}
如图所示这里面的抽象方法满足了以下的几个条件:
//接口 方法名 == User.xml 中 id 名
//返回值类型 与 Mapper.xml文件中返回值类型要一致
//方法的入参类型 与Mapper.xml中入参的类型要一致
//命名空间 绑定此接口
其中第四个命名空间绑定此接口的意思是说,命名空间应该是UserMapper的接口,那么此时才可以自动创建
select * from user where id = #{value}
之后我们就可以直接通过这个接口来创建代理对象,然后调用xml中配置好的sql语句了:
public void test1() throws IOException {//加载核心配置文件String resource = "sqlMapConfig.xml";InputStream in = Resources.getResourceAsStream(resource);//创建SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();//SqlSEssion帮我生成一个实现类 (给接口)UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.findUserById(10);System.out.println(user);}
注意这里其实是在测试,真实的是SqlSessionFactory传给dao层,Dao层只需要根据UserMapper来创建一个实现类,这个实现类会自动帮助我们实现这个接口内的方法,我们只需要调用对应的方法就可以了,和之前我们写的一样,这样我们就只用写接口就好了。
SqlMapConfig.xml配置文件
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
我们主要使用properties、typeAliases、mappers,其中properties可以配置一个配置文件,然后在xml中就可以使用这个配置文件中的内容了
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8jdbc.username=rootjdbc.password=root
typeAliases可以配置别名
这里是将com.huanfeng.bean.User的别名设置为User,那么在mapper这些xml文件中就可以直接使用user了.实际中bean下面可能有多个javaBean,如果全部这样设置也有一些复杂,所以可以从包的角度来进行设计
如果这样设计了,那么bean下面类的别名就是这个类的类名,所以后面我们可以直接将com.huanfeng.bean.User的别名看成是User,user也可以了,别名大小写不敏感
除了我们自定义的这些类的别名,mybatis还会java中本身自带的一些类进行了别名设置,
byte Byte long Long short Short int Integer integer Integer double Double float Float boolean Boolean date Date decimal BigDecimal bigdecimal BigDecimal mapMap
这就解释了为什么我们前面直接使用Integer了。
1.1. mappers(映射器)
Mapper配置的几种方法:
我们前面学习了一种:
还有一种方式,通过类来配置:
此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
和之前的别名配置一样,如果有多个mapper,那么就要配置多次,为了解决这个问题,我们可以使用包的配置方式:
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。这样mybatis就会自动去找了,和接口一样名称的配置文件了。一般常用这种方式