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全局配置文件
-
全局配置文件位置
-
全局配置文件
<?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 批量插入