Mybatis操作详解 一、Mybatis的Maven工程操作

1.1、导入依赖

<!--mysql驱动坐标-->
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>5.1.6</version>
   </dependency>
<!-- mybatis坐标 -->
   <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.5.2</version>
   </dependency>
<!--日志坐标-->
   <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.12</version>
   </dependency>

1.2、创建Mybatis全局配置文件

  • 全局配置文件位置

Mybatis操作详解_sql

  • 全局配置文件

<?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>
 <properties resource="datasourse.properties"></properties>
 <!-- properties引入外部properties配置文件内容 resourse 引入类路径下的文件,url 引入网络或者磁盘路径下的资源-->
 
 <environments default="development">
 <!-- mybatis可以配置多种环境,mysql和oracle,可以用defaul指定使用环境,实现环境的快速切换 -->
   <environment id="development">
     <transactionManager type="JDBC"/>
     <dataSource type="POOLED">
       <property name="driver" value="${driver}"/>
       <property name="url" value="${url}"/>
       <property name="username" value="${username}"/>
       <property name="password" value="${password}"/>
     </dataSource>
   </environment>
 </environments>
 <mappers>
 <!-- mapper:注册一个sql映射
  注册配置文件
            resourse:引用类路径下的sql映射文件
            url:引用网络或者磁盘路径
  注册接口
  class:引用(注册)接口,
  1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
  2、没有SQL映射文件,所有的sql都是利用注解写在接口上
  例如:
      @Select("select * from t_user where u_id=#{u_id}")
  public User queryById(int id);
    注意:写在配置文件内方便维护,若是不用维护,较少使用的才用注解。
    -->
   <mapper resource="mapper/UserMapper.xml"/>
 </mappers>
</configuration>

<!-- 对应的datasourse.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/studentsys?useUnicode=true&characterEncoding=UTF-8
username=root
password=123 -->
  • 其它相关配置标签

<!-- 是重要的设置项,name表示设置项名,value设置项取值 -->
<settings>
   <setting name="" value=""></setting>
</settings>
 
<!-- 别名处理器:可以为java类型起别名,type是全类名,alias是新起的别名 -->
<typeAliases>
   <typeAlias type="" alias=""/>
   <!-- package为某个包下所有的类批量起别名,name指定包名(所有类起一个默认别名(类名小写)) -->
   <package name="">
</typeAliases>
@Alias("emp") //别名注释,写在类上给类起别名

1.3、创建接口(interface)

public interface UserDao{
   public User queryById(int u_id);
   
   public int addUser(int u_id,String u_username,String u_sex);
   
   public int delUser(int u_id);
   
   public int updateUser(int u_id,String u_sex,String u_username);
}

 

1.4、映射文件—实现:增删改查

  • 必须有对应的接口的文件,映射文件用于实现接口并完成关系映射

<?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">
<!-- namespcace 命令空间,指定映射的接口文件 -->
<mapper namespace="dao.UserDao">
<!-- id:方法名称
parameterType:参数类型
int
String
resultType:表示返回类型,
若是查询一般是映射的对象。。。
若是增删改允许定义Integer(表示影响行数)、Long、void、Boolean(表示是否成功,只要一条成功就true)
增删改查返回值不在配置文件中设置,直接在接口方法中设置
  public boolean addUser(User user);

public int delUser(int id);
-->
<select id="queryById" parameterType="_int"
resultType="pojo.User">
select * from t_user where u_id=#{u_id}
</select>
<insert id="addUser">
insert into t_user values(#{u_id},#{u_username},#{u_sex})
</insert>
<delete id="delUser">
delete from t_user where u_id=#{u_id}
</delete>
   <update id="updateUser">
  update user set u_id=#{u_id},u_sex=#{u_sex} where username=#{u_username};
   </update>
</mapper>

1.5、运行程序,操作数据库

1.5.1、步骤:

  • 使用全局配置文件得到SqlSessionFactory

  • 使用SqlSessionFactory,获取SqlSession来进行增删改查,一个SqlSession代表与数据库的一次会话,用完需要关闭。

  • 根据此方法自动为接口创建一个代理对象,去实现增删改查功能:

    • UserDao userdao = session.getMapper(UserDao.class);

  • SqlSession和connection都是非线程安全,因此每次都要创建新的对象

public class MybatisQueryTest {

public static void main(String[] args) throws IOException {
//1-加载配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2-获取工厂
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(inputStream);
//3-从工厂拿取session
SqlSession session = factory.openSession();
//4-从session里面拿取mapper
UserDao userdao = session.getMapper(UserDao.class);
//5-业务处理
User user = userdao.queryById(1001);
System.out.println(user);
  //6、关闭资源
      session.close();  
}

}

1.5.2、事务提交—增删改

  • SqlSession session = factory.openSession();默认为手动提交,

  • 必须添加 session.commit(); 否则会回滚

  • 可以设置为自动提交:factory.openSession(true);

public class MybatisDelTest {

public static void main(String[] args) throws IOException {
//1-加载配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2-获取工厂
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(inputStream);
//3-从工厂拿取session
SqlSession session = factory.openSession();
//4-从session里面拿取mapper
UserDao userdao = session.getMapper(UserDao.class);
//5-业务处理
int num = userdao.delUser(1015);
System.out.println(num);
//7-提交事务,否则回滚
session.commit();
  //6、关闭资源
      session.close();
}
}
二、ORM对象关系映射

定义:Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。

2.1、传入参数

2.1.1、传入多个参数

1)默认名称

接口信息:

public User query(String name,String sex);   //接口方法

映射文件:

 <!-- 形参会封装为一个map,有默认的key,映射文件可以直接取用 -->
<select id="query" resultType="pojo.User">
select * from t_user where u_name=#{param1} and u_sex=#{param2}
</select>

2)自定义名称

接口信息:

//若是形参多,不易辨认,可以通过@Param("name")来映射,这样封装map时的key可以自己设定

public User query(@Param("name") String name,@Param("sex") String sex); //接口方法

映射文件:

 <select id="query" resultType="pojo.User">
select * from t_user where u_name=#{name} and u_sex=#{sex}
</select>

2.1.2、传入pojo对象

若多个形参均为数据模型pojo的属性,直接传入pojo

传入数据类型设置:parameterType="pojo全类名或者别名",也可以不写,直接使用#{属性}调用属性值

接口信息:

//如果多个参数正好是数据模型pojo,我们可以设置模型参数,直接传入pojo

public boolean addUser(User user);  //接口方法

映射文件:

<!-- 若传入的参数为pojo,#{属性值},直接写属性值 -->

<select id="queryMan" resultMap="user">
select * from t_user where u_sex=#{sex}
</select>

方法调用:

//创建user
User user = new User();
user.setSex("男");
//业务处理
List<User> list = userdao.queryMan(user);

2.1.3、传入Map(万能)

如果多个参数正好是数据模型pojo,我们可以设置模型参数,直接传入pojo

传入数据类型设置:parameterType="map",也可以不写,直接使用#{key}调用value

接口信息:

public User querytree(Map<String,Object> map);  //接口方法

映射文件:

<!-- 若传入的参数为pojo,#{属性值},直接写属性值 -->
<select id="conditionQuery" resultType="pojo.User" parameterType="map">
select * from t_user where u_name=#{name} and u_sex=#{sex}
</select>

方法调用:

//业务处理
Map<String,Object> map = new HashMap();
map.put("name", "自来也");
map.put("sex", "男");
User user = userdao.querytree(map);
System.out.println(user);

2.1.4、传入TO(Transfer Object)数据传输对象

Page{
   int index;
   int size;
}

2.1.5、注意事项

  • 形参若传入对象,对象的属性在映射文件中也能使用

#{Object.属性}来获取
  • Collection(List、set)以及数组也会封装进map,List和数组还能通过索引调用

#{List[0]}   //数组或者list均可如此
  • #{ }和${ }之间的区别

    • #{ }:以预编译的形式,将参数设置到sql语句中,起PreperStatement的功效,可以防止sql注入。本质就是jdbc里面的?,一个占位符。

    • ${ }:是一个直接映射过来的数据,JDBC很多地方不支持占位符,一般只是参数使用占位符?,因此需要拼接语句的地方,必须使用${ }。

    • #{ }有更丰富的用法:

      规定参数的一些规则:

      javaType、jdbcType、mode(存储过程)......

      oracle不能识别null类型
      需要指定类型
      #{email,jdbcType=NULL}   默认jdbcType=OTHER

       

2、获取返回类型

2.2.1、返回类型为List

接口信息:

//接口设置返回数据为List<>

public List<User> queryMan(String sex);

映射文件:

<!-- 这里的结果类型不能写List,写List内部要封装的类型 -->

<select id="queryMan" resultType="pojo.User">
  select * from t_user where u_sex=#{u_sex}
</select>

2.2.2、返回一个Map

优势:查询多字段,返回一个Map,并且可以指定某属性作为key

接口信息:

//指定某字段作为key
@MapKey("u_id")
public Map<Integer,User> queryWoman(String sex);

映射文件:

 <select id="queryWoman" resultType="map">    //这里指定的结果类型为为map
select * from t_user where u_sex=#{u_sex}
</select>

3、添加返回数据映射

<!-- 数据库返回的字段与返回结果之间的映射 -->
<resultMap type="pojo.User" id="ouruser">
   <id column="u_id" property="id"/>
   <result column="u_username" property="username"/>
   <result column="u_password" property="password"/>
   <result column="u_name" property="name"/>
   <result column="u_tel" property="tel"/>
   <result column="u_flag" property="flag"/>
   <result column="u_image" property="image"/>
   <result column="u_login_time" property="logintime"/>
   <result column="u_sex" property="sex"/>
   <result column="u_age" property="age"/>
</resultMap>

<!-- resultType 换做 resultMap,值写成result 的 id -->
<select id="queryUser"  parameterType="pojo.User" resultMap="ouruser">
    select * from t_user where u_sex='男' and u_name=#{name}
</select>
三、关联查询

3.1、关联查询添加返回数据类型

public class CourseInfo {
   private Integer id;
   private String date;
   
   //Course的引用数据类型
   private Course course;
   
   //Staff的引用数据类型
   private Staff teacher;
}

public class Course {
   private Integer coId;
   private String coName;
   private String coType;
   private Integer coMaxstu;
   private String coPrice;
   private String coOncetime;
   private String coImage;
   private String coDescribe;
   private String coBeizhu;
   private Integer coFlag;
}

public class Staff {
   private Integer staffId;
   private String staffName;
   private String staffGender;
   private String staffTel;
   private String staffJob;
   private String staffPower;
   private String stadium;
   private String isLogin;
   private String staffState;
}

3.1.1、直接映射

<resultMap id="schedule" type="com.hxzy.course.pojo.Schedule">
   <id column="arrange_id" property="id"/>
   <result column="date" property="date"/>

   <!-- 直接:   引用数据类型属性.属性 -->
   <id column="co_id" property="course.coId"/>
   <result column="co_name" property="course.coName"/>
   <result column="co_type" property="course.coType"/>
   <result column="co_maxstu" property="course.coMaxstu"/>
   <result column="co_price" property="course.coPrice"/>
   <result column="co_oncetime" property="course.coOncetime"/>
   <result column="co_image" property="course.coImage"/>
   <result column="co_describe" property="course.coDescribe"/>
   <result column="co_beizhu" property="course.coBeizhu"/>
   <result column="co_flag" property="coFlag"/>

   <!-- 直接:   引用数据类型属性.属性 -->
   <result column="staff_id" property="teacher.staffId"/>
   <result column="staff_name" property="teacher.staffName"/>
   <result column="staff_gender" property="teacher.staffGender"/>
   <result column="staff_tel" property="teacher.staffTel"/>
   <result column="staff_job" property="teacher.staffJob"/>
   <result column="staff_power" property="teacher.staffPower"/>
   <result column="stadium" property="teacher.stadium"/>
   <result column="is_login" property="teacher.isLogin"/>
   <result column="staff_state" property="teacher.staffState"/>
   <id column="ti_id" property="tiId"/>
   <result column="ti_time" property="teacher.tiTime"/>
</resultMap>

3.1.2、< association >标签映射

<resultMap id="schedule" type="com.hxzy.course.pojo.Schedule">
   <id column="arrange_id" property="id"/>
   <result column="date" property="date"/>
   <association property="course" javaType="com.hxzy.course.pojo.Course">
       <id column="co_id" property="coId"/>
       <result column="co_name" property="coName"/>
       <result column="co_type" property="coType"/>
       <result column="co_maxstu" property="coMaxstu"/>
       <result column="co_price" property="coPrice"/>
       <result column="co_oncetime" property="coOncetime"/>
       <result column="co_image" property="coImage"/>
       <result column="co_describe" property="coDescribe"/>
       <result column="co_beizhu" property="coBeizhu"/>
       <result column="co_flag" property="coFlag"/>
   </association>
   <association property="teacher" javaType="com.hxzy.course.pojo.Staff">
       <id column="staff_id" property="staffId"/>
       <result column="staff_name" property="staffName"/>
       <result column="staff_gender" property="staffGender"/>
       <result column="staff_tel" property="staffTel"/>
       <result column="staff_job" property="staffJob"/>
       <result column="staff_power" property="staffPower"/>
       <result column="stadium" property="stadium"/>
       <result column="is_login" property="isLogin"/>
       <result column="staff_state" property="staffState"/>
   </association>
</resultMap>

3.1.3、< collection >关联查询集合封装

<!-- collection表示返回的类型是个集合 -->
<!-- ofType指定集合内部存放的对象 -->
<collection property="course" ofType="pojo.Course">
   <id column="c_id" property="id"></id>
   <result column="c_name" property="name"></result>
   <result column="c_address" property="address"></result>
   <result column="c_flag" property="flag"></result>
</collection>

3.1.4、关联查询

<!-- 这里返回类型同样使用resultMap -->
<select id="getTuankeSchedule" resultMap="schedule">
  SELECT * FROM arrange_course
  JOIN course ON arrange_course.co_id = course.co_id
  JOIN staff ON arrange_course.staff_id = staff.staff_id
  WHERE arrange_course.co_id IN (SELECT co_id FROM course WHERE co_type = '团课')
  AND arrange_course.ti_id IS NOT NULL AND arrange_course.DATE IS NOT NULL
</select>

 

3.2、分步查询

如何分步:

  • 对象的封装分为多步,首先查询封装非引用数据类型

  • 再进行通过已查询数据,获取引用数据类型的相关数据

<mapper namespace="com.hxzy.course.mapper.ScheduleMapper">
   <resultMap id="schedule" type="com.hxzy.course.pojo.Schedule">
       <id column="arrange_id" property="id"/>
       <result column="date" property="date"/>
       <result column="co_id" property="coId"/>
       <result column="staff_id" property="staffId"/>
       
       <!-- 在接口内添加一个queryCourseById的方法 -->
       <!-- select这个引用数据是通过某个接口内的方法查询得到的 -->
       <!-- column是指定传入查询语句的参数 -->
       <association property="course" select="com.hxzy.course.mapper.ScheduleMapper.queryCourseById" column="co_id">
           <id column="co_id" property="coId"/>
           <result column="co_name" property="coName"/>
           <result column="co_type" property="coType"/>
           <result column="co_maxstu" property="coMaxstu"/>
           <result column="co_price" property="coPrice"/>
           <result column="co_oncetime" property="coOncetime"/>
           <result column="co_image" property="coImage"/>
           <result column="co_describe" property="coDescribe"/>
           <result column="co_beizhu" property="coBeizhu"/>
           <result column="co_flag" property="coFlag"/>
       </association>
   </resultMap>
   
   <select id="getTuankeSchedule" resultMap="schedule">
      SELECT * FROM arrange_course
      WHERE arrange_course.co_id IN (SELECT co_id FROM course WHERE co_type = '团课')
      AND arrange_course.ti_id IS NOT NULL AND arrange_course.DATE IS NOT NULL
   </select>
   
   <select id="queryCourseById" resultType="com.hxzy.course.pojo.Course">
      SELECT * FROM course
      WHERE course.co_id = #{co_id}
   </select>
</mapper>

 

3.3、延迟查询

定义:在上述引用对象被使用时才会被查询。

实现方式:

  • 使用了分步查询

  • 对默认配置进行两个修改

在全局文件中进行设置

<settings>
   <!-- 表示关联的值在使用的时候才会被查询加载,需要开启 -->
<setting name="lazyLaodingEnabled" value="true"></setting>
   <!-- 表示我们所填属性会被完整加载,需要关闭 -->
   <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

完成这两个配置后,我们的分步查询就变为延迟查询。分步查询只做第一步,后续查询步骤被使用时才会进行

四、动态sql

作用:处理复杂查询逻辑时,需要动态生成sql语句,这一步可以在java后台自己进行拼接。而Mybatis已经提供了完整的SQL语句拼接功能。

ps:下面所有的实现方法,传入的形参均为pojo;一般传入的是pojo或者map,可以使用属性名和key来获取属性值和value

4.1、< if >标签

< if >标签,判断为真,将查询语句拼入

<select id="findUserByCondition" parameterMap="user" resultMap="user">
select * from user where type='团课'
   <!-- 传入的是对象,直接输入属性 -->
   <if test="id!=0">
  and id=#{id}
   </if>
   <if test="username!=null">
  and username=#{username}
   </if>
</select>

4.2、< where >标签

作用:< if >标签的最常用应用场景为条件查询,而最开头为where,后面是and,拼接相对不容易。< where >会自行判断,并删除第一个 and 或者 or

  • 解决方式一

<select id="findUserByCondition" parameterMap="user" resultMap="user">
<!-- 直接写 where 1=1 ,后面全部写 and -->
  select * from user where 1=1
   <if test="id!=0">
  and id=#{id}
   </if>
   <if test="username!=null">
  and username=#{username}
   </if>
</select>
  • 解决方式二

<select id="findUserByCondition" parameterMap="user" resultMap="user">
select * from user
   <!-- 使用where标签,它会自己判断,并消除第一个 and 或 or -->
   <where>
       <if test="id!=0">
      and id=#{id}
       </if>
       <if test="username!=null">
      or username=#{username}
       </if>
   </where>
</select>

4.3、< trim >标签

作用:用于解决拼接整体后的前后缀问题:

< trim >的四个属性:

  • prefix:给拼接后的字符串加一个前缀

  • prefixOverrides:给拼接后的字符串去掉一个前缀

  • suffix:给拼接后的字符串添加一个后缀

  • suffixOverrides:给拼接后的字符串去掉一个后缀

<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>

条件查询的解决方案三:

<!-- 表示在拼接后的字符串前方添加一个where,在后方去掉一个and -->
<trim prefix="where" suffixOverrides="and">
   <if test="id!=0">
  id=#{id} and
   </if>
   <if test="username!=null">
  username=#{username} and
   </if>
</trim>

4.4、< choose >分支语句标签

作用:前端页面是一个输入框,输入框可以输入姓名、ID、或者其它信息,输入一条信息,但是存在多种可能,这时候的处理就需要分支语句

和< if >的区别:< choose >只能选择其中一条路走,< if >会都进行判断

<choose>
   <when test="course!=0">
      where course.co_id=#{course}
   </when>
   <when test="teacher!=0">
      where staff.staff_id=#{teacher}
   </when>
   <otherwise>
      where 1=1
   </otherwise>
</choose>

4.5、< set >标签

作用:修改属性时,去除后面的逗号。这里可以使用< trim >,或者直接用 mybatis-plus 更方便

<update id="updateUser">
  update user
   <set>
  <if test="u_id!=null">
      u_id=#{u_id},
       </if>
       <if test="u_sex!=null">
      u_sex=#{u_sex},
       </if>
   </set>
  where username=#{u_username};
</update>

4.6、< foreach >标签

作用:遍历传入的集合

  • collection:指定要遍历的集合

  • item:将当前遍历出的元素赋值给指定变量,用#{变量名}能取出变量值即当前遍历值

  • separator:每个元素直接的分隔符

  • open:遍历后的结果拼接一个开头字符

  • close:遍历后的结果拼接一个结尾字符

  • index:索引

    • 遍历list的时候,index是索引,item是当前值

    • 遍历map的时候,index是key,item是map的值

<select id="findUserByIds" parameterType="list" resultMap="user">
  select * from user where id in
   <foreach collection="array" open="(" close=")" item="id"
            separator=",">
      #{id}
   </foreach>
</select>

4.6.1、mysql环境下的 foreach 批量插入

应用场景:< foreach >标签也能用于数据批量插入

 

4.6.2、oracle环境下的 foreach 批量插入