Mabatis的概述


JavaEE开发是分层的:表现层 业务层 持久层

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。
mybatis框架
明确:它是一个持久层框架,解决项目对数据库的CRUD操作,只要会方法名、sql语句,学mybatis框架很轻松。 什么是ORM?
ORM :Object Relational Mapping 对象关系映射(目的:操作对象,就可以操作数据库)
通过建立数据库表和Java实体类的对应关系,从而实现操作实体类就相当于操作数据库表。
ORM思想对应的框架有:mybatis,hibernate,spring data jpa
现阶段的数据访问层框架。 
Mybatis框架入门
开发环境的准备及统一
1.检查JDK环境 
2.检查Tomcat环境(忽略,springmvc用到)
3.检查maven环境 
4.2导入Maven依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.it.mybatis003</groupId><artifactId>mybatis00302</artifactId><version>1.0-SNAPSHOT</version><!--需要添加的是以下部分--><dependencies><!--数据库依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.45</version></dependency><!--mybatis依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><!--测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--日志依赖--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies></project>
4.3创建数据库
创建数据库 DROP TABLE IF EXISTS user;
CREATE TABLE `user` (`id` int(11) NOT NULL auto_increment,`username` varchar(32) NOT NULL COMMENT '用户名称',`birthday` datetime default NULL COMMENT '生日',`sex` char(1) default NULL COMMENT '性别',`address` varchar(256) default NULL COMMENT '地址',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
导入数据
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');
4.4创建实体类
创建包:com.it.domain
创建类:User.java
public class User implements Serializable {private int id;// 主键IDprivate String username;// 用户姓名private String sex;// 性别private Date birthday;// 生日private String address;// 地址此处省略getset ostring方法
4.5创建接口UserDao.java
创建接口,用来操作数据库
创建包com.it.dao
创建接口UserDao.java
public interface UserDao {/*** 查询所有*/List<User> findAll();}
也有人将它命名UserMapper,这个是因人而异,我们为了大家的编程习惯,叫做Dao。
4.6创建sqlMapConfig.xml
在resources包下,新建sqlMapConfig.xml 
sqlMapConfig.xml
为了更好将数据库连接信息抽取出来,我们原来在C3P0连接池中也已经将数据库连接信息抽取出来,我们现在也一样将数据库的连接信息抽取出来,放到一个xml(SqlMapConfig.xml)文件中,后面再去对此配置文件进行xml解析,这样就可以将配置文件中的信息读取出来,以便在jdbc代码中直接使用这些数据库连接信息

<!--初始版--><?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!--配置mybatis的环境--><environments default="mysql"><environment id="mysql"><!--配置事务管理策略--><transactionManager type="JDBC"></transactionManager><!--配置数据源--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///itcastmybatis"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!--配置映射文件的信息--><mappers><mapper resource="com/it/dao/UserDao.xml"></mapper></mappers></configuration>
更新版:↓
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!--这里是后面的知识用外部配置文件的方式导入数据库信息--><properties resource="jdbcconfig.properties"></properties><!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写--><typeAliases><package name="com.it.domain"></package></typeAliases><!--配置mystis的环境--><environments default="mysql"><environment id="mysql"><!--固定配置事务管理器为JDBC--><transactionManager type="JDBC"></transactionManager><!--固定配置数据源为POOLED--><dataSource type="POOLED"><!--当使用外部配置文件是需要用${}引用--><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><!--配置接口与映射文件的信息与默认的区别为后面同包名下的地址前缀无需写和不区分大小写--><mappers><package name="com.it.dao"></package></mappers></configuration>
4.7创建UserDao.xml
因为是maven项目,所有xml的文件应该放在resources下,创建包com.it.dao(要注意此包名结构需与上面的接口包名结构一致),创建UserDao.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.it.dao.UserDao"><!--查询所有--><select id="findAll" resultType="com.it.domain.User">select * from user</select></mapper>
4.8创建测试类
在test文件夹下,创建包com.it.test,创建类MyBatisTest.java
public class MyBatisTest {/*** 测试mybatis的环境搭建*/public static void main(String[] args) throws IOException {// 1.读取配置文件InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");// 2.根据配置文件构建SqlSessionFactorySqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);// 3.使用SqlSessionFactory创建SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 4.使用SqlSession构建Dao的代理对象UserDao userDao = sqlSession.getMapper(UserDao.class);// 5.执行dao的findAll方法List<User> list = userDao.findAll();// 第4,第5步骤可以写成// List<User> list = sqlSession.selectList("com.it.dao.UserDao.findAll");for(User user:list){System.out.println(user);}// 5.关闭资源sqlSession.close();in.close();}}
最后如果没有发现日志,引入日志包,在resources文件下创建log4j.properties
附件为日志文件,可下载后拖入即可
保存
UserDao.java
为了实现新增操作,我们可以在原有入门示例的UserDao.java类中添加一个用于saveUser()的方法用于用户新增操作。
UserDao类中新增saveUser()方法,如下:
/*** 保存用户* @param user*/void saveUser(User user);
UserDao.xml
在UserDao.xml文件中加入新增用户的配置,如下:
添加标签
<!--保存用户--><insert id="saveUser" parameterType="com.it.domain.User">insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});</insert>
我们可以发现,这个sql语句中使用#{}字符,#{}代表占位符,我们可以理解是原来jdbc所学的?,它们都是代表占位符, 具体的值是由User类的username属性来决定的。使用OGNL表达式
parameterType属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
注意:
这种方式要求<mapper namespace=”映射接口所在的包名+类名”>,同时还要求<select>,<insert>,<delete>,<update>这些标签中的id属性一定与代理接口中的方法名相同。
MybatisTest.java
测试初始化:
- 读取SqlMapConfig.xml
- 构建SqlSessionFactory
- 打开SqlSession
- 获取UserDao对象
测试结束 - 提交事务
- 释放资源
这样方便测试。
/*** 测试mybatis的crud操作*/public class MybatisTest {private InputStream in;private SqlSession sqlSession;private UserDao userDao;@Before//用于在测试方法执行之前执行public void init()throws Exception{//1.读取配置文件,生成字节输入流in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.获取SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.获取SqlSession对象sqlSession = factory.openSession();//4.获取dao的代理对象userDao = sqlSession.getMapper(UserDao.class);}@After//用于在测试方法执行之后执行public void destroy()throws Exception{//提交事务sqlSession.commit();//6.释放资源sqlSession.close();in.close();}/*** 测试查询所有*/@Testpublic void testFindAll(){//5.执行查询所有方法List<User> users = userDao.findAll();for(User user : users){System.out.println(user);}}/*** 测试保存操作*/@Testpublic void testSave(){User user = new User();user.setUsername("吴邪");user.setAddress("洛阳");user.setSex("男");user.setBirthday(new Date());System.out.println("保存操作之前:"+user);//5.执行保存方法userDao.saveUser(user);System.out.println("保存操作之后:"+user);}}
如果发现测试没有添加任何记录,原因是什么?
这一点和jdbc是一样的,我们在实现增删改时一定要去控制事务的提交,那么在mybatis中如何控制事务提交呢?
可以使用:session.commit();来实现事务提交。加入事务提交后的代码如下:
但是:这时候发现保存操作之前User对象的id和保存操作之后User对象的id都为null,如果我们想在保存之后获取User对象的id呢?
问题扩展:新增用户id的返回值
新增用户后,同时还要返回当前新增用户的id值,因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长auto_increment的值返回。
Mysql自增主键的返回,配置如下:
insert into user(username,address,sex,birthday) values('张三','深圳','女','2018-07-24');select last_insert_id()
配置:UserDao.xml
<!--保存用户--><insert id="saveUser" parameterType="com.it.domain.User"><!-- 配置插入操作后,获取插入数据的id --><selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">select last_insert_id();</selectKey>insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});</insert>
测试类MyBatisTest.java
/*** 测试保存操作*/@Testpublic void testSave(){User user = new User();user.setUsername("吴邪");user.setAddress("海底墓");user.setSex("男");user.setBirthday(new Date());System.out.println("保存操作之前:"+user);//5.执行保存方法userDao.saveUser(user);System.out.println("保存操作之后:"+user);}
修改
UserDao.java
/*** 更新用户* @param user*/void updateUser(User user);
5.3.2UserDao.xml
<update id="updateUser" parameterType="com.it.domain.User">update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}</update>
5.3.3MybatisTest.java
/*** 测试更新操作*/@Testpublic void testUpdate(){User user = new User();user.setId(50);user.setUsername("如花2");user.setAddress("中粮商务公园");user.setSex("男");user.setBirthday(new Date());//5.执行保存方法userDao.updateUser(user);}
删除
UserDao.java
/*** 根据Id删除用户* @param userId*/void deleteUser(Integer userId);
5.4.2UserDao.xml
<!-- 删除用户--><delete id="deleteUser" parameterType="java.lang.Integer">delete from user where id = #{uid}</delete>
其中的#{uid}是占位符,代表参数的值由方法的参数传入进来的。
注意:
1.此处的#{uid}中的id其实只是一个形参,所以它的名称是自由定义的,比如定义成#{abc}也是可以的。
2.关于parameterType的取值问题,对于基本类型我们可以直接写成int,short,double…..也可以写成java.lang.Integer。
3.字符串可以写成string,也可以写成java.lang.String
也就是说:int是java.lang.Integer的别名
string是java.lang.String的别名
别名是不区分大小写
5.4.3MybatisTest.java
/*** 测试删除操作*/@Testpublic void testDelete(){//5.执行删除方法userDao.deleteUser(48);}
主键查询
UserDao.java
/*** 根据id查询用户信息* @param id* @return*/User findById(Integer id);
5.5.2UserDao.xml
<!-- 根据id查询用户 --><select id="findById" parameterType="INT" resultType="com.it.domain.User">select * from user where id = #{uid}</select>
5.5.3MybatisTest.java
/*** 测试主键ID查询操作*/@Testpublic void testFindOne(){//5.执行查询一个方法User user = userDao.findById(50);System.out.println(user);}
模糊查询
现在来实现根据用户名查询用户信息,此时如果用户名想用模糊搜索的话,我就可以想到前面Web课程中所学的模糊查询来实现。
UserDao.java
可以在UserDao类中添加一个findByName()的方法,如下:
/*** 根据名称模糊查询用户信息* @param name* @return*/List<User> findByName(String name);
UserDao.xml
下面在UserDao.xml文件中加入模糊查询的配置代码,如下:
<!-- 根据名称模糊查询 --><select id="findByName" parameterType="string" resultType="com.it.domain.User">select * from user where username like #{name}</select>
注意:此时的#{name}中的因为这时候是普通的参数,所以它的起名是随意的,比如我们改成#{abc}也是可以的。
MybatisTest.java
/*** 测试模糊查询操作*/@Testpublic void testFindByName(){//5.执行查询一个方法List<User> users = userDao.findByName("%王%");for(User user : users){System.out.println(user);}}
我们在UserDao.xml配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标识%。配置文件中的#{name}也只是一个占位符,所以SQL语句显示为“?”。
如何将模糊查询的匹配符%写到配置文件中呢?
模糊查询的另一种配置方式
第一步:编写UserDao.xml文件,配置如下:
<!-- 根据名称模糊查询 --><select id="findByName" parameterType="string" resultType="com.it.domain.User">select * from user where username like '%${value}%'</select>
我们在上面将原来的#{}占位符,改成了{value}的写法就是固定的,不能写成其它名字。
第二步:编写测试方法,如下:
/*** 测试模糊查询操作*/@Testpublic void testFindByName(){//5.执行查询一个方法// List<User> users = userDao.findByName("%王%");List<User> users = userDao.findByName("王");for(User user : users){System.out.println(user);}}
#{}与${}的区别
#{}表示一个占位符号
通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串
通过{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{value}呢?我们一起来看TextSqlNode类的源码:
这就说明了源码中指定了读取的key的名字就是”value”,所以我们在绑定参数时就只能叫value的名字了。
定义resultMap
由于上边的userDao.xml中sql查询列和Users.java类属性不一致,需要定义resultMap:
第一步:修改UserDao.xml
<!-- 查询所有 --><select id="findAll" resultMap="userMap"><!--select id as userId,username as userName,address as userAddress,sex as userSex,birthday as userBirthday from user; -->select * from user;</select>
第二步:在UserDao.xml中的
<!-- 配置 查询结果的列名和实体类的属性名的对应关系 --><resultMap id="userMap" type="com.it.domain.User"><!-- 主键字段的对应 --><id property="userId" column="id"></id><!--非主键字段的对应--><result property="userName" column="username"></result><result property="userAddress" column="address"></result><result property="userSex" column="sex"></result><result property="userBirthday" column="birthday"></result></resultMap>
<id />:此属性表示查询结果集的唯一标识,非常重要。如果是多个字段为复合唯一约束则定义多个<id />。 property:表示User类的属性。 column:表示sql查询出来的字段名。 column和property放在一块儿表示将sql查询出来的字段映射到指定的pojo类属性上。 <result />:普通属性(普通字段),即pojo的属性。
QueryVo.java
开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。 Pojo类中包含pojo。
需求:根据用户名查询用户信息,查询条件放到QueryVo的user属性中。
在com.it.domain中,创建QueryVO.java
public class QueryVo {private User user;public User getUser() {return user;}public void setUser(User user) {this.user = user;}}
6.2.3UserDao.java
/*** 根据queryVo中的条件查询用户* @param vo* @return*/List<User> findUserByVo(QueryVo vo);
6.2.4UserDao.xml
<!-- 根据queryVo的条件查询用户 --><select id="findUserByVo" parameterType="com.it.domain.QueryVo" resultType="com.it.domain.User">select * from user where username like #{user.username}</select>
如果我们使用的是包装类作为参数,比如这个示例的QueryVo类作为findUserByVo()方法的参数,那么在使用时,因为QueryVo类中有一个User类的user对象,而这个user对象中才能找到username属性,所以我们在访问属性时,就使用OGNL表达式才访问对象的属性,即#{user.username}。
MybatisTest.java
/*** 测试使用QueryVo作为查询条件*/@Testpublic void testFindByVo(){QueryVo vo = new QueryVo();User user = new User();user.setUsername("%王%");vo.setUser(user);//5.执行查询一个方法List<User> users = userDao.findUserByVo(vo);for(User u : users){System.out.println(u);}}