一,mybatis介绍
1,MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
2,MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
3,mabatis和hibernate的比较:mybatis是轻量级的半orm的持久层框架,hibernate是重量级的全orm框架;hibernate适用于需求变更不频繁的中小型框架,如后台管理系统等等,mybatis适用于需求变化较大的项目,如互联网项目
二,mybatis的配置文件:mybatis.xml,需要在这里加载mybatis运行环境,创建sqlsessionfactory(线程安全的,实际使用时用单例管理)工厂,用sqlsessionfactory创建的sqlsession是线程不安全的,一般在方法体中使用
1,properties(在mybatis.xml中加载属性文件)
注意: MyBatis 将按照下面的顺序来加载属性:
Ⅰ 在 properties 元素体内定义的属性首先被读取。
Ⅰ 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
Ⅰ 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。
因此,通过parameterType传递的属性具有最高优先级,resource或 url 加载的属性次之,最低优先级的是 properties 元素体内定义的属性。
Ⅰ 所以不建议在propertie标签下定义属性,在properties属性文件中的属性一定具有特殊性,如XXXXXXX.XXXXX
<!--加载属性文件-->
<properties resource="db/db.properties">
<!--在这里也可以配置属性-->
<property name="" value=""></property>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!--数据源-->
<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>
2,全局参数配置,会影响mybatis的运行行为
3,typeAliases(类型别名,mybatis默认支持一些别名)
Ⅰ在mapper的xml文件中有好多paramterType和resultType,每次要输入全名,不方便开发
<!--别名定义-->
<typeAliases>
<!--单个别名定义-->
<!--<typeAlias type="com.study.bean.Goods" alias="goods"></typeAlias>-->
<!--批量别名定义
指定一个包名:mybatis就会自动扫描包名下的pojo,定义别名,别名就是类名,首字母可大写可小写
-->
<package name="com.study.bean"></package>
</typeAliases>
使用:
<select id="selectGoodsById" parameterType="int" resultType="goods">
SELECT * FROM goods WHERE id=#{id}
</select>
4,typeHandlers(类型处理器)
mybatis通过typeHandlers类型处理器用于java类型和jdbc类型映射
通常mybatis所自带的类型处理器已经满足日常需要,不需要再额外定义
4,mappers(映射配置)
Mapper配置的几种方法:
1
<mapper resource=" " />
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
2
<mapper url=" " />
使用完全限定路径
如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
3
<mapper class=" " />
使用mapper接口类路径
如:<mapper class="com.study.mapper.UsersMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
4
<package name=""/>
注册指定包下的所有mapper接口(批量加载)
如:
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
三,mybatis测试需要根据log4j在后台打印sql语句去测试(log4j.properties)
log4j.properties
#开发环境设置成debug
log4j.rootLogger=debug,stdout
log4j.additivity.org.apache=true
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.Console.Threshold=trace
#log4j.logger.org.hibernate.hql.ast.AST=debug
###log just the SQL
log4j.logger.org.hibernate.SQL=info
###log JDBC bind parameters ###
log4j.logger.org.hibernate.type=trace
#log4j.logger.org.hibernate.type=debug
###log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=debug
三,mybatis入门程序以及测试(这里直接上mapper代理的方式,原始dao的开发方法就不写了)
1,实体类
package com.study.bean;
import java.io.Serializable;
public class Goods implements Serializable{
private Integer id;
private String goodsname;
private String godsfullname;
private Integer num;
private Integer typeid;
public Goods() {
super();
}
public Goods(Integer id, String goodsname, String godsfullname, Integer num, Integer typeid) {
this.id = id;
this.goodsname = goodsname;
this.godsfullname = godsfullname;
this.num = num;
this.typeid = typeid;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getGoodsname() {
return goodsname;
}
public void setGoodsname(String goodsname) {
this.goodsname = goodsname;
}
public String getGodsfullname() {
return godsfullname;
}
public void setGodsfullname(String godsfullname) {
this.godsfullname = godsfullname;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public Integer getTypeid() {
return typeid;
}
public void setTypeid(Integer typeid) {
this.typeid = typeid;
}
}
2,mapper接口
package com.study.mapper;
import com.study.bean.Goods;
import java.util.List;
public interface GoodsMapper {
// 提供调用方法,在xml中实现
/**
* mybatis中查询一般select开头,其他的update,insert,delete开头
*但是不是必须的
* mapper接口中方法参数只能为一个,可以使用包装类型
*
* */
public Goods selectGoodsById(Integer id);
public List<Goods> selectGoodsByName(String name);
public void insertGoods(Goods goods);
public void deleteGoods(Integer id);
public void updateGoods(Goods goods);
}
3,实现在mapper的xml文件(GoodsMapper.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">
<!-- <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"> -->
<!--通过接口调用xml中的方法
-->
<mapper namespace="com.study.mapper.GoodsMapper">
<!--select查询标签,id为实现方法,将sql语句封装到mappedStatement对象中,所以id称为Statement的id
parameterType为参数名称,resultType指定输出结果单挑记录映射的java类型(单挑多条都可以写com.study.bean.Goods)-->
<!--
里面写sql语句
#{}代表一个占位,防止sql注入
${} 表示拼接字符串,将字符串不加任何修饰的拼接在sql中,可能会有sql注入
${value}:接收输入参数的名称,若传入类型为简单类型则只能使用value
-->
<select id="selectGoodsById" parameterType="int" resultType="com.study.bean.Goods">
SELECT * FROM goods WHERE id=#{id}
</select>
<select id="selectGoodsByName" parameterType="java.lang.String" resultType="com.study.bean.Goods">
SELECT * FROM goods WHERE goodsname LIKE '%${value}%';
</select>
<!--添加用户
参数类型parameterType为pojo
#{}指定pojo的属性名,接受到pojo对象的属性值,mybatis也是ognl获取对象的属性值
-->
<!--selectKey将插入的主键返回到goods对象中
SELECT LAST_INSERT_ID()获取刚刚插入数据的主键,只适用于自增主键
keyProperty:将查询到的主键值设置在parameterType的哪个属性
order:SELECT LAST_INSERT_ID()相对于insert语句的执行顺序
-->
<insert id="insertGoods" parameterType="com.study.bean.Goods" >
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO goods (goodsname,godsfullname,num,typeid) VALUES (#{goodsname},#{godsfullname},#{num},#{typeid})
</insert>
<!--非自增主键的获取方法
使用mysql的uuid()来生成主键
执行过程:首先通过uuid生成主键,将主键放在对象的id中
在select语句执行时,通过getId获取对象中的id
-->
<!--<insert id="insertGoods" parameterType="com.study.bean.Goods" >
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT uuid()
</selectKey>
INSERT INTO goods (id,goodsname,godsfullname,num,typeid) VALUES (#{id},#{goodsname},#{godsfullname},#{num},#{typeid})
</insert>-->
<!--删除-->
<delete id="deleteGoods" parameterType="int">
DELETE FROM goods WHERE id=#{id}
</delete>
<!--根据id更新
id值必须存在
-->
<update id="updateGoods" parameterType="com.study.bean.Goods">
UPDATE goods SET godsfullname=#{godsfullname},num=#{num},typeid=#{typeid},goodsname=#{goodsname} WHERE id=#{id}
</update>
</mapper>
4,测试
package com.study.test;
import com.study.bean.Goods;
import com.study.mapper.GoodsMapper;
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 java.io.IOException;
import java.io.Reader;
import java.util.List;
/**
* @author: Demon
* @date: 2019/2/11
* @time: 21:04
* Description:
*/
public class RunTest {
public static void main(String[] args) throws IOException{
// 手动加载mybatis配置
String resource = "mybatis.xml";
Reader reader = Resources.getResourceAsReader(resource);
// 创建会话工厂SqlSessionFactory,传入mybatis的配置问价信息
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
// 获取sqlSession
SqlSession sqlSession = ssf.openSession();
// 获得接口对象实例
GoodsMapper goodsMapper=sqlSession.getMapper(GoodsMapper.class);
/*Goods goods=goodsMapper.selectGoodsById(1);
System.out.println(goods.getGodsfullname());*/
/*List<Goods> list=goodsMapper.selectGoodsByName("包");
for (Goods goods:list){
System.out.println(goods.getGoodsname());
}*/
// 添加
/* Goods goods=new Goods();
goods.setGoodsname("卫龙");
goods.setGodsfullname("中国卫龙");
goods.setNum(13);
goods.setTypeid(1);
goodsMapper.insertGoods(goods);
System.out.println(goods.getId());*/
// 删除
/*goodsMapper.deleteGoods(10);*/
// 更新
Goods goods=new Goods();
goods.setId(5);
goods.setGoodsname("卫龙");
goods.setGodsfullname("中国卫龙");
goods.setNum(13);
goods.setTypeid(1);
goodsMapper.updateGoods(goods);
sqlSession.commit();
sqlSession.close();
}
}
四,parameterType和resultType
1,parameterType
开发中有时会用到pojo传递查询条件,查询条件是综合的,不仅包括闪商品查询条件,还包括其他查询条件,这是可以使用包装pojo传递参数
1,定义包装对象
goods:
package com.study.bean;
import java.io.Serializable;
public class Goods implements Serializable{
private Integer id;
private String goodsname;
private String godsfullname;
private Integer num;
private Integer typeid;
public Goods() {
super();
}
public Goods(Integer id, String goodsname, String godsfullname, Integer num, Integer typeid) {
this.id = id;
this.goodsname = goodsname;
this.godsfullname = godsfullname;
this.num = num;
this.typeid = typeid;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getGoodsname() {
return goodsname;
}
public void setGoodsname(String goodsname) {
this.goodsname = goodsname;
}
public String getGodsfullname() {
return godsfullname;
}
public void setGodsfullname(String godsfullname) {
this.godsfullname = godsfullname;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public Integer getTypeid() {
return typeid;
}
public void setTypeid(Integer typeid) {
this.typeid = typeid;
}
}
GoodsUser
package com.study.bean;
public class GoodsUser extends Goods{
}
GoodsQueryVo(包装类型)
package com.study.bean;
public class GoodsQueryVo {
// 包装类型,里面写查询条件
private GoodsUser goodsUser;
public GoodsUser getGoodsUser() {
return goodsUser;
}
public void setGoodsUser(GoodsUser goodsUser) {
this.goodsUser = goodsUser;
}
}
2,mapper文件和mapper.xml映射文件中:
public List<GoodsUser> selectGoodsList(GoodsQueryVo goodsQueryVo);
<!--用户综合查询-->
<select id="selectGoodsList" parameterType="GoodsQueryVo" resultType="GoodsUser">
SELECT * FROM goods where goodsname= #{goodsUser.goodsname} and godsfullname like '%${goodsUser.godsfullname}%'
</select>
说明:mybatis底层通过ognl从pojo中获取属性值:#{goodsUser.goodsname},goodsUser即是传入的包装对象的属性。queryVo是别名,即上边定义的包装对象类型。
2,resultType和resultMap
resultType:
使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。
1,输出简单类型:查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。
2,pojo对象或pojo列表
不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。
生成的动态代理对象中是根据mapper方法的返回值类型确定是调用selectOne(返回单个对象调用)还是selectList (返回集合对象调用 ).
resultMap:
mybatis中使用resultMap完成高级输出结果映射。
如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。
1、定义resultMap
<!--定义resultMap,输出的结果和对象中的属性名不一致的时候使用
如果此resulMap在其他的xml中,需要加namespace
type:resultMap最终映射的java对象类型,可以使用别名
id:唯一标识
-->
<resultMap id="goodsResultMap" type="Goods">
<!--
id:查询结果集中的唯一标识
column:查询出来的列名
property:type指定pojo类型的最终属性名
-->
<id column="id_" property="id"/>
<!--
result:对普通列的定义
-->
<result column="goodsname_" property="goodsname"/>
</resultMap>
2、使用resultMap作为statement的输出映射类型
<!--resultMap使用:-->
<select id="findResultMap" parameterType="int" resultMap="goodsResultMap">
SELECT id id_,goodsname goodsname_ from goods WHERE id=#{id}
</select>
五,动态sql和sql片段
mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
1,动态sql(用类似if的标签可以判断是否为空,若为空不进行拼接)
<!--用户综合查询-->
<select id="selectGoodsList" parameterType="GoodsQueryVo" resultType="GoodsUser">
SELECT * FROM goods
<!--where可以自动去掉第一个查询语句中的and-->
<where>
<if test="goodsUser!=null">
<if test="goodsUser.goodsname!=null and goodsUser.goodsname!=''">
and goodsname= #{goodsUser.goodsname}
</if>
<if test="goodsUser.godsfullname!=null and goodsUser.godsfullname!=''">
and godsfullname like '%${goodsUser.godsfullname}%'
</if>
</if>
</where>
</select>
2,sql片段
将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。
方便程序员进行开发。
<!--定义sql片段
id:为sql片段的唯一标识
基于单表来定义sql片段,这样sql片段的可重用行才高-->
<sql id="query_goods_where">
<if test="goodsUser!=null">
<if test="goodsUser.goodsname!=null and goodsUser.goodsname!=''">
and goodsname= #{goodsUser.goodsname}
</if>
<if test="goodsUser.godsfullname!=null and goodsUser.godsfullname!=''">
and godsfullname like '%${goodsUser.godsfullname}%'
</if>
</if>
</sql>
<!--sql片段使用-->
<select id="selectGoodsList" parameterType="GoodsQueryVo" resultType="GoodsUser">
SELECT * FROM goods
<where>
<!--引用sql 片段,如果sql片段不在本mapper中,则加入namespace-->
<include refid="query_goods_where"></include>
<!--这里可以加入其他的引用-->
</where>
</select>