MyBatis
一、概念
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
二、安装
1) mybatis依赖
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2)在idea中设置mybatis配置文件
mybatis-config.xm和mybatis-mapper.xml的l文件(包含了对 MyBatis 系统的核心设置)
mybatis-config.xml文件内容
<?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>
<typeAliases>
<package name=""/>
</typeAliases>
<environments default="">
<environment id="">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value=""/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
</mappers>
</configuration>
mybatis-mapper.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">
<mapper namespace="">
</mapper>
3)配置文件属性的描述
mybatis-config.xml文件内容描述
mybatis-config.xml存放在 resources目录下
<?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">
<!-- 这个是mybatis的核心文件 -->
<configuration>
<!-- typeAliases用于配置实体类的别名 -->
<typeAliases>
<!-- 方式一:为每一个实例类分别指定一个别名,使用时不区分大小写 -->
<!-- <typeAlias type="end.nf.mybatis.entity.User" alias="user"/> -->
<!-- 方式二:直接指定实体类的包,这样mybatis就会自动给这个包下的所有的实体类自动创建别名,别名就是类名(不包括包名),并且使用时也不区分大小写 -->
<package name="edu.nf.mybatis.entity"/>
</typeAliases>
<!-- environments(配置环境):主要是是配置不同数据库的数据源 ,default指定使用的默认数据源环境的id-->
<environments default="mysql">
<!-- environment:配置MySQL数据源的环境,id通常指定数据库的名 -->
<environment id="mysql">
<!-- transactionManager:事务管理使用JDBC的事务 -->
<transactionManager type="JDBC"/>
<!-- type指定为pool表示数据源连接使用连接池 -->
<!-- 目前是可以复用的 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/pms?useUnicode=true&characterEncoding=utf-8&useSSL=false&timeZone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="wyy"/>
</dataSource>
</environment>
</environments>
<!-- 可以配置多个MySQL数据源 -->
<!-- mappers:指定映射配置文件 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
mybatis-mapper.xml文件内容描述
小乌鸦的插件:MyBatisX
<?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">
<!-- 映射配置文件:通常以(类名+Mapper)创建名称,并且放入resources下的子mappers文件夹中 -->
<!-- namespace:指定Dao接口的完整类名 -->
<mapper namespace="edu.nf.mybatis.dao.UserDao">
<!-- 给UserDao接口中的所有方法编写相应的SQL语句 -->
<!-- insert表示插入语句,对应的删除和修改就是update和delete标签 -->
<!-- id:指定的方法名,用于绑定SQL语句 -->
<!-- parameterType:指定参数的类型(edu.nf.mybatis.entity.User),可以引用别名 -->
<!-- 在<insert>标签中,不能编写任何注释,只能编写sql语句 -->
<!-- sql语句中的参数使用#{实体的属性名}来获取实体对象中属性的值 -->
<!-- parameterType:对应的事dao接口的参数类型
实体类参数:使用完整类名或实体别名
map参数:map
list或array参数:collection
基本数据类型参数:int、float、long...
字符串参数:string-->
<!-- parameterType:对应的事dao接口的参数类型
实体类参数:使用完整类名或实体别名
map参数:map
list或array参数:collection
基本数据类型参数:int、float、long...
字符串参数:string-->
<insert id="save" parameterType="user">
insert into user(uname,age) values(#{uname},#{age});
</insert>
<!-- 如果参数是map,那么ognl表达式的值为map的key -->
<insert id="save2" parameterType="map">
insert into user(uname,age) values(#{uname},#{age});
</insert>
<update id="update" parameterType="user">
update user set uname = #{uname} where uid = #{uid};
</update>
<!-- 删除 -->
<!-- 注意:当前方法参数只有一个并且是一个普通类型的时候,那么ognl的值可以任意填写 -->
<delete id="delete" parameterType="int">
delete from user where uid = #{id};
</delete>
</mapper>
三、使用mybatis
1)创建MyBatisUtil工具类
注意:手动提交事物与自动提交事物的区别
手动提交事务:执行多条SQL语句时 - 比如:转账、批量增删改查等操作
自动提交事物:只执行一条SQL语句
public class MyBatisUtils {
/**
* 声明SqlSessionFactory,用于创建SqlSession实体
*/
private static SqlSessionFactory sqlSessionFactory;
/**
* 在静态代码块中初始化SqlSessionFactory工厂
*/
static {
// 1.通过查询MyBatis的核心文件,先创建一个用于读取xml的文件输入流
try {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
// 2.将输入流传给创建的SqlSessionFactoryBuilder进行解析,并初始化
// 整个SqlSessionFactory工厂对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
throw new RuntimeException("Init mybatis error." , e);
}
}
/**
* SqlSession是操作数据库的核心对象,相当于封装了整个Connection
* 不带参数的openSession方法的到的SqlSession是需要我们手动提交事物的
* 手动提交事物:执行多个sql - 比如:转账、批量增删改查
* @return
*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
/**
* openSession方法还可以传入一个boolean类的的参数
* 如果设置为true,创建出来的SqlSession就会自动提交事务
* 反之,false一样是受同行提交事物
* 自动提交事物:执行1个sql
* @param autoCommit 是否自动提交
* @return
*/
public static SqlSession getSqlSession(Boolean autoCommit){
return sqlSessionFactory.openSession(autoCommit);
}
}
2)编写实体类、Dao等
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer uid;
private String uname;
private Integer age;
}
public interface UserDao {
/**
* 添加用户
* @param user
*/
void save(User user);
/**
* 添加用户
* @param map
*/
void save2(Map<String ,Object> map);
}
3)实现类
public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
// 先创建SqlSession - 放在try中,就不需要我们手动去关闭SqlSession资源
SqlSession session = MyBatisUtils.getSqlSession();
try {
// 执行保存操作
// getMapper:获取接口的class,调用这个方法
session.getMapper(UserDao.class).save(user);
// 手动提交事务
session.commit();
} catch (RuntimeException e) {
e.printStackTrace();
// 事务的回滚:手动提交事务的时候需要事务回滚
session.rollback();
} finally {
session.close();
}
}
@Override
public void save2(Map<String, Object> map) {
SqlSession session = MyBatisUtils.getSqlSession(true);
try {
session.getMapper(UserDao.class).save2(map);
} catch(RuntimeException e){
e.printStackTrace();
} finally {
session.close();
}
}
}
4)映射配置文件
注意:映射配置文件:通常以(类名+Mapper)创建名称,并且放入resources下的子mappers文件夹中
四、UUID和雪花ID
一、UUID
1、生成UUID
public class UUIDUtils {
public static String createUUID(){
String uuid = UUID.randomUUID().toString();
// 去除UUID的“-” :把“-”改为null字符
return uuid.replace("-","");
}
public static void main(String[] args) {
System.out.println(createUUID());
}
}
2、在mybatis中使用UUID
<?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">
<mapper namespace="edu.nf.ch02.dao.StudentDao">
<!-- 使用UUID作为主键
方式一:使用Java的UUID工具类来生成uuid插入数据库 - (推荐)
方式二:基于不同数据库的uuid生成函数来创建uuid,
例如MySQL的生成函数是uuid(),因此在执行sql前可以利用此函数生成一个uuid -->
<!-- selectKey的keyProperty指定通过uuid()函数乘车的id先保存到实体的stuId字段中
resultType:返回的字段类型
order设置为before表示为在执行insert语句之前先执行select uuid(),然后在执行insert语句,此时的insert语句中的stuId是已经存在值的了 -->
<insert id="saveStu" parameterType="student">
<selectKey keyProperty="stuId" resultType="string" order="BEFORE">
select uuid();
</selectKey>
insert into stu_info(stu_id, stu_name) values (#{stuId}, #{stuName});
</insert>
<!-- 当数据量非常大的时候会进行分库分表,
此时自增的id回来来业务上的问题(不同的表会产生相同的id值),
因此可以考虑采用雪花ID,生成雪花ID可以使用开源的工具类生成(例如:hutool)
雪花ID是一个长度为64的long类型的id-->
</mapper>
二、雪花ID
1、概念
世界上,没有完全相同的两片雪花。这就是雪花算法这个名称的由来。雪花算法(SnowFlake)是Twitter公司发明的一种算法,主要目的是解决在分布式环境下,ID怎样生成的问题。
在正常的业务开发中,数据库中的主键或者唯一键,都需要生成一个唯一标识,如用户id、订单id、订单编号等。
那么雪花算法是如何保证在高并发的情况下,能够保证不重复呢?
雪花ID的组成
在Java中表示雪花算法生成的id,需要使用Long类型,雪花算法是由1位符号位,0表示正数,1表示负数,id一般都是正数。
接下来的41位为一个毫秒级时间戳,这个时间戳存储的并不是当前时间,而是当前时间戳与指定的时间戳之间的差值,即指定时间戳 - 当前时间戳。
10位的数据机器位,可以表示2的10次方,即1024个节点,其中包括五位数据中心id和五位工作节点id。
12位毫秒内的计数器,支持统一机器同一时间戳产生4096个id序号。
以上数据合在一起刚好64位,即一个Long型数据。
2、使用雪花ID
1、添加hutool依赖方便我们调用API方法
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
2、生成雪花ID
public class SnowFlakeUtils {
public static long create(){
return IdUtil.getSnowflakeNextId();
}
public static void main(String[] args) {
System.out.println(create());
System.out.println(create());
System.out.println(create());
}
}
3、在mybatis中使用雪花ID
<?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">
<!-- namespace指定Dao的完整类名 -->
<mapper namespace="edu.nf.ch02.dao.UserDao">
<!-- useGeneratedKeys设置为true表示要获取自增长的key,
并通过keyProperty将取出来的id保存到实体的uid字段中 -->
<insert id="saveUser" parameterType="user" useGeneratedKeys="true"
keyProperty="uid">
insert into user_info(u_name, u_tel) values(#{userName}, #{tel})
</insert>
<!-- 使用UUID作为主键 -->
</mapper>
五、多参数查询
多条件参数查询, 不需要指定parameterType
使用方法:
1. 当有多个参数时,ognl表达式中只能使用下标(例如:param1,param2)来绑定参数 2. 使用@Param注解来绑定 - 常用
<?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">
<mapper namespace="edu.nf.ch03.dao.UserDao">
<!-- resultType指定结果集返回映射的数据类型,
注意:查询语句中使用“as 别名”来映射到实体类-->
<!--也可以使用mybatis提供的驼峰命名,需要再mybatis核心配置文件启用,
这样当实体类字段是规范的驼峰命名法,mybatis就会自动将查询的字段转换为驼峰映射-->
<select id="getUserById" parameterType="int" resultType="user">
select user_id, user_name, user_tel from user_info where user_id = #{uid}
</select>
<select id="getUserById2" parameterType="int" resultType="map">
select user_id,user_name,user_tel from user_info where user_id = #{uid}
</select>
<!-- 查询用户列表 -->
<select id="listUsers" resultType="user">
select user_id, user_name, user_tel from user_info
</select>
<!-- 统计查询 -->
<select id="countUser" resultType="int">
select count(*) from user_info
</select>
<!-- 多条件参数查询, 不需要指定parameterType,因为parameterType只能指定一个参数的
当有多个参数时,ognl表达式中只能使用下标(例如:param1,param2)来绑定参数
或者使用@Param注解来绑定-->
<select id="getUserById3" resultType="user">
select user_id, user_name, user_tel from user_info
where user_name = #{uname} and user_tel = #{tel}
</select>
<!-- 声明resultMap用于User实体类和user_info的表映射-->
<resultMap id="userMap" type="user">
<!-- id用于映射主键,property指定实体的字段名,column指定表列的名称 -->
<id property="userId" column="user_id"/>
<!-- 其他属性使用result标签来映射-->
<result property="userName" column="user_name"/>
<result property="userTel" column="user_tel"/>
</resultMap>
<!-- 使用resultMap映射查询结果集,对应上面id的值 -->
<select id="listUsers2" resultMap="userMap">
select user_id, user_name, user_tel from user_info
</select>
</mapper>
使用注解
public interface UserDao {
/**
* 多条件查询
* @param name
* @param age
* @return
*/
User getUserById3(@Param("name") String name, @Param("age") int age);
}
驼峰命名法
<?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>
<!-- 启用驼峰命名,将数据库字段的下划线转驼峰模式
例如:user_name 自动转换为 userName -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 实体的别名 -->
<package name="edu.nf.ch03.entity"/>
</typeAliases>
<!-- 数据源环境配置 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/pms?useUnicode=true&characterEncoding=utf-8&useSSL=false&timeZone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="wyy"/>
</dataSource>
</environment>
</environments>
<!-- 映射配置文件 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
六、多表查询
1、多对一 and 多对多
多对一(查询一个对象):使用resultMap实现关联映射的是
多对多(查询一个集合):使用resultMap实现关联映射的是
<?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">
<mapper namespace="edu.nf.ch04.dao.StuDao">
<!-- 使用resultMap实现关联映射 -->
<resultMap id="stuMap" type="edu.nf.ch04.entity.Student">
<!-- 映射主键 -->
<id property="stuId" column="stu_id"/>
<!-- 映射其他字段-->
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
<!-- 多对一,班级映射 - 查询一个对象就使用<association>-->
<association property="classInfo">
<!-- 映射班级信息 -->
<id property="cid" column="c_id"/>
<result property="className" column="class_name"/>
</association>
<!-- 多对多映射住址 - 查询一个集合就使用<collection>-->
<collection property="addresses" ofType="edu.nf.ch04.entity.Address">
<id property="aid" column="a_id"/>
<result property="address" column="address"/>
</collection>
</resultMap>
<select id="getStuById" parameterType="int" resultMap="stuMap">
select s.stu_id, s.stu_name, s.stu_age, c.class_name, a.address
from stu_info s join class_info c on s.c_id = c.c_id join stu_addr sa
on s.stu_id = sa.stu_id join addr_info a on a.a_id = sa.addr_id
where s.stu_id = #{sid};
</select>
</mapper>
2、一对多
一对多关联学生, 一对多和多对多使用collection映射, 查询时注意要查询一这一方的id
<?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">
<mapper namespace="edu.nf.ch04.dao.ClassDao">
<resultMap id="classMap" type="edu.nf.ch04.entity.ClassInfo">
<id property="cid" column="c_id"/>
<result property="className" column="class_name"/>
<!-- 一对多关联学生, 一对多和多对多使用collection映射, 查询时注意要查询一这一方的id -->
<!-- ofType用于指定映射集合中的实体类型-->
<collection property="students" ofType="edu.nf.ch04.entity.Student">
<id property="stuId" column="stu_id"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
</collection>
</resultMap>
<select id="getClassInfoById" parameterType="int" resultMap="classMap">
select c.c_id, c.class_name, s.stu_name, s.stu_age from class_info c
join stu_info s on c.c_id = s.c_id where c.c_id = #{cid}
</select>
</mapper>
3、一对一
<?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">
<mapper namespace="edu.nf.ch04.dao.IdCardDao">
<resultMap id="cardMapper" type="edu.nf.ch04.entity.IdCard">
<id property="cardId" column="card_id"/>
<result property="cardNum" column="card_num"/>
<!-- 一对一关联学生 -->
<association property="student">
<id property="stuId" column="stu_id"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
</association>
</resultMap>
<select id="getCardByNum" parameterType="string" resultMap="cardMapper">
select c.card_id, c.card_num, s.stu_name, s.stu_age from card_info c
join stu_info s on s.stu_id = c.stu_id where c.card_num = #{cardNum}
</select>
</mapper>
七、动态查询
部分Dao案列
public interface StuDao {
/**
* 多条件查询
* 多个条件可以使用实体类封装也可以使用map集合封装
* @param params
* @return
*/
List<Student> listStudents(Map<String, Object> params);
/**
* 多条件动态查询
* @param params
* @return
*/
List<Student> listStudents2(Map<String, Object> params);
/**
* 根据年龄范围查询
* @param ages
* @return
*/
List<Student> listStudents3(List<Integer> ages);
List<Student> listStudents4(Integer[] ages);
/**
* 修改操作
* @param stu
*/
void updateStu(Student stu);
/**
* 批量更新操作
* @param params
*/
void batchAdd(List<Student> params);
/**
* 批量删除
* @param stuId
*/
void batchDelete(List<Integer> stuId);
}
在mybatis中使用
<?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">
<mapper namespace="edu.nf.ch05.dao.StuDao">
<!-- 定义resultMap映射查询结果集 -->
<resultMap id="stuMap" type="edu.nf.ch05.entity.Student">
<id property="stuId" column="stu_id"/>
<result property="stuName" column="stu_name"/>
<result property="stuAge" column="stu_age"/>
</resultMap>
<!-- 动态条件查询(多个条件),结合<where>标签和<if>标签来实现 -->
<select id="listStudents" parameterType="map" resultMap="stuMap">
select stu_id,stu_name,stu_age from student_info
<where>
<if test="uname != null and uname != ''">
stu_name = #{uname}
</if>
<if test="age != null">
and stu_age = #{age}
</if>
</where>
order by stu_id desc;
</select>
<!-- 动态条件选择,使用<choose>标签 -->
<!-- <when>标签:多个条件,当第一个满足条件后,就不管后面的条件 -->
<!-- <otherwise>标签:不管前面的条件成不成立,能都执行<otherwise>标签里的语句 -->
<select id="listStudents2" parameterType="map" resultMap="stuMap">
select stu_id,stu_name,stu_age from student_info
<choose>
<when test="uname != null and uname != ''">
where stu_name = #{uname}
</when>
<when test="age != null">
where stu_age = #{age}
</when>
<otherwise>
order by stu_id desc;
</otherwise>
</choose>
</select>
<!-- 使用<forEach>标签循环参数,运用于or或in子句查询操作 -->
<!-- 如果使用list,那么使用的时候list -->
<select id="listStudents3" parameterType="collection" resultMap="stuMap">
select stu_id,stu_name,stu_age from student_info
<where>
stu_age in
<if test="list != null">
<foreach collection="list" item="age" open="(" separator="," close=")">
#{age}
</foreach>
</if>
</where>
</select>
<!-- 注意:当参数是数组的时候,parameterType的值collection(collection包括list集合、数组等) -->
<!-- 如果使用数组,那么使用的是array -->
<select id="listStudents4" parameterType="collection" resultMap="stuMap">
select stu_id,stu_name,stu_age from student_info
<where>
stu_age in
<if test="array != null">
<foreach collection="array" item="age" open="(" separator="," close=")">
#{age}
</foreach>
</if>
</where>
</select>
<!-- 动态更新,使用<set>标签实现动态更新字段 -->
<update id="updateStu" parameterType="edu.nf.ch05.entity.Student">
update student_info
<set>
<if test="stuName != null and stuName != ''">
stu_name = #{stuName},
</if>
<if test="stuAge != null">
stu_age = #{stuAge}
</if>
</set>
where stu_id = #{stuId}
</update>
<!-- 批量添加 -->
<insert id="batchAdd" parameterType="collection">
insert into student_info(stu_name,stu_age) values
<foreach collection="list" item="stu" separator=",">
(#{stu.stuName},#{stu.stuAge})
</foreach>
</insert>
<!-- 批量删除:同理于范围查询 -->
<delete id="batchDelete" parameterType="collection">
delete from student_info
where stu_id in
<if test="list != null">
<foreach collection="list" item="stuId" open="(" separator="," close=")">
#{stuId}
</foreach>
</if>
</delete>
</mapper>
测试代码
public class StuDaoTest {
@Test
public void listStudentsTest() {
StuDao dao = new StuDaoImpl();
Map<String, Object> map = new HashMap<>();
// map.put("uname","张三");
map.put("age", 18);
List<Student> list = dao.listStudents(map);
list.forEach(stu -> System.out.println(stu.getStuName()));
}
@Test
public void listStudents2Test() {
StuDao dao = new StuDaoImpl();
Map<String, Object> map = new HashMap<>();
map.put("uname", "张三");
map.put("ag e", 18);
List<Student> list = dao.listStudents2(map);
list.forEach(stu -> System.out.println(stu.getStuName()));
}
@Test
public void listStudents3Test() {
StuDao dao = new StuDaoImpl();
List<Integer> age = Arrays.asList(19, 20, 21);
List<Student> list = dao.listStudents3(age);
list.forEach(stu -> System.out.println(stu.getStuName()));
}
@Test
public void listStudents4Test() {
StuDao dao = new StuDaoImpl();
Integer[] ages = {19, 21};
List<Student> list = dao.listStudents4(ages);
list.forEach(stu -> System.out.println(stu.getStuName()));
}
@Test
public void updateStuTest() {
StuDao dao = new StuDaoImpl();
Student stu = new Student();
stu.setStuName("盈盈");
stu.setStuAge(22);
stu.setStuId(1);
dao.updateStu(stu);
}
@Test
public void batchAddTest() {
StuDao dao = new StuDaoImpl();
Student stu1 = new Student();
stu1.setStuName("张三丰");
stu1.setStuAge(18);
Student stu2 = new Student();
stu2.setStuName("wangl");
stu2.setStuAge(22);
List<Student> list = Arrays.asList(stu1,stu2);
dao.batchAdd(list);
}
@Test
public void batchDeleteTest() {
StuDao dao = new StuDaoImpl();
List<Integer> list = Arrays.asList(6,7);
dao.batchDelete(list);
}
}
八、使用注解
注解的使用:
1.使用@Options注解来获取增长的主键
2.在多表查询的时候,需要现在mapper.xml中声明resultMap,然后使用@ResultMap注解
注解的value指定为“dao接口的完整类名,resultMap的id”
3.当在dao接口上使用注解映射sql时,在mybatis.xml中配置映射文件时,可以使用
或
注意:只能二选一
public interface StuDao {
/**
* 添加
*
* @param student
*/
@Insert("insert into student_info(stu_name, stu_age) values (#{stuName},#{age})")
// 使用Options注解来获取自增长的主键
@Options(useGeneratedKeys = true, keyProperty = "uid")
void save(Student student);
/**
* 修改
*
* @param student
*/
@Update("update student_info set stu_name = #{stuName},stu_age = #{age} where stu_id = #{uid}")
void update(Student student);
/**
* 删除
* @param uid
*/
@Delete("delete from student_info where stu_id = #{uid}")
void delete(int uid);
/**
* 查询
* 方式一:使用as别名
* 方式二:使用驼峰命名法,前提是(在数据库是用的是stu_id,那么在实体中定义字段的时候应该使用stuId)
* @param uid
* @return
*/
@Select("select stu_id as uid,stu_name as stuName,stu_age as age from student_info where stu_id = #{uid}")
@ResultType(Student.class)
Student getStudentById(int uid);
/**
* 查询所以的班学生信息和班级信息
* @return
*/
@Select("select s.stu_id,s.stu_name,s.stu_age, c.class_name from student_info s " +
"join class_info c on c.c_id = s.c_id")
// 如果使用@ResultMap注解,那么映射配置还是放在xml中
// 注解的value指定为“dao接口的完整类名,resultMap的id”
@ResultMap("edu.nf.ch06.dao.StuDao.stuMap")
List<Student> listStudents();
}
查询:
方式一:使用as别名
方式二:使用驼峰命名法,前提是(在数据库是用的是stu_id,那么在实体中定义字段的时候应该使用stuId,以此类推)
<?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">
<mapper namespace="edu.nf.ch06.dao.StuDao">
<resultMap id="stuMap" type="edu.nf.ch06.entity.Student">
<id property="uid" column="stu_id"/>
<result property="stuName" column="stu_name"/>
<result property="age" column="stu_age"/>
<!-- 关联班级 -->
<association property="classInfo">
<id property="cid" column="c_id"/>
<result property="className" column="class_name"/>
</association>
</resultMap>
</mapper>
<?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>
<typeAliases>
<package name="edu.nf.ch06.entity"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/pms?useUnicode=true&characterEncoding=utf-8&useSSL=false&timeZone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="wyy"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 映射配置文件,当如果使用了mapper映射,里面会指定namespace的dao完整类名,下面的dao接口就不需要在指定了 -->
<mapper resource="mappers/StudentMapper.xml"/>
<!-- 当在dao接口上使用注解映射sql时,这里就使用class属性指dao接口的完整类名 -->
<!-- <mapper class="edu.nf.ch06.dao.StuDao"/>-->
<!-- 也可以使用package标签指定dao接口的包即可 -->
<!-- <package name="edu.nf.ch06.dao"/>-->
</mappers>
</configuration>
九、分页查询
1、内存查询
内存查询:是mybatis中提供了一个RowBuonds对象来进行简单的内存分页查询
这种方式是将全部数据查询出来缓存到内存中,接着在内存中进行分页逻辑
注意:当数据量大的时候这样方式不推荐,
案列:
public interface CityDao {
/**
* 内存分页
* mybatis提供了一个RowBounds对象来进行简单的内存分页
* 这种方式是将全部数据查询出来缓存到内存中,接着在内存中进行分页逻辑(当数据量大的时候这种方式不推荐)
* @param rowBounds
* @return
*/
List<City> listCity(City city, RowBounds rowBounds);
}
public class CityDaoTest {
@Test
public void listCity(){
CityDao dao = new CityDaoImpl();
// 创建分页对象RowBounds
RowBounds rb = new RowBounds(0,10);
City city = new City();
city.setProvince("广东");
List<City> list = dao.listCity(city,rb);
list.forEach(c -> System.out.println(c.getCityName()));
}
}
<!--查询整张表的数据-->
<select id="listCity" resultType="edu.nf.ch07.entity.City">
select city_id as cityId,city_name as cityName,city_code as cityCode,province from city_info
<where>
<if test="province != null and province != ''">
province = #{province}
</if>
</where>
</select>
2、物理查询
物理查询:使用PageHelper分页,这种分页方式是物理查询
PageHelper插件会根据当前使用的数据库类型动态
生成相应的分页语句,因为在编写sql时无需考虑分页的sql方言
使用@Param注解标识当前页(pageNum)和每页显示记录数(pageSize)
注意:使用分页时,一定要使用pageNum和pageSize为注解名
1)依赖pagehelper插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.3</version>
</dependency>
2)在mybatis.xml中配置pagehelper插件
<!-- 配置插件 -->
<plugins>
<!-- 配置PageHelper分页插件 -->
<!-- PageInterceptor(分页拦截器),主要将原来地收起来 语句进行拦截处理,根据当前使用的数据库类型动态生成相应的分页语句 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 指定数据库方言 -->
<property name="helperDialect" value="mysql"/>
<!-- 启用分页参数的注解支持 -->
<property name="supportMethodsArguments" value="true"/>
<!-- 分页合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
3)案列
public interface CityDao {
/**
* 使用PageHelper分页,这种分页方式是物理分页,
* PageHelper插件会根据当前使用的数据库类型动态
* 生成相应的分页语句,因为在编写sql时无需考虑分页的sql方言
* 使用@Param注解标识当前页(pageNum)
* 和每页显示记录数(pageSize)
* @param city
* @param pageNum 第几页
* @param pageSize 每页几天
* @return
*/
List<City> listCity2(@Param("city") City city, @Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize);
}
<?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">
<mapper namespace="edu.nf.work04.dao.CityDao">
<!-- 使用@Param注解绑定查询参数,或使用下标(param1、param2...) -->
<select id="listCity" resultType="edu.nf.work04.entity.City">
select city_id as cityId,city_name as cityName,city_code as cityCode,province from city_info
<where>
<if test="city.province != null and city.province != ''">
province = #{city.province}
</if>
<if test="city.cityName != null and city.cityName != ''">
and city_name = #{city.cityName}
</if>
</where>
</select>
</mapper>
public class CityDaoTest {
@Test
public void listCityTest(){
CityDao dao = new CityDaoImpl();
City city = new City();
city.setProvince("广东");
// city.setCityName("番禺");
List<City> list = dao.listCity(city, 1, 10);
// 创建分页信息对象
PageInfo<City> pageInfo = new PageInfo<>(list);
System.out.println(list);
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("上一页:" + pageInfo.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("每页记录数:" + pageInfo.getPageSize());
System.out.println("总记录数:" + pageInfo.getTotal());
}
}