day10
Mybatis集合操作
问题展现: 查询 id = 1 ,id = 2, id =4,id = 5,id = 6的用户信息
sql语句: select * from user where id in (1,2,4,5,6)
难点: 用户传过来的可能不是单个数值,而是数组, List集合, Map集合等
在< select >< /select > 中使用 < foreach >< /foreach > 标签
<!--
任务:用户传递的是数组 遍历数组
collection: 要遍历的集合信息
1.数组 array
2.List集合 list
3.Map中的key
open="" close="" 整体标签的开始和结束
item: 每次循环遍历的变量 类似于for循环中i变量 与后文的 #{ } 中的内容对应
index: 遍历数据的下标
-->
<select id="selectIdsByMap" resultType="com.atguigu.mybatis.pojo.User">
select * from user where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
数组Array
接收数组用作sql语句查询
test代码
private SqlSessionFactory sqlSessionFactory;
//简化的init代码
@Before
public void init() throws IOException {
String configName = "mybatis-config.xml";
InputStream resource = Resources.getResourceAsStream(configName);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
}
@Test
public void test01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int[] ids = {1,2,4,5,6};
List<User> userList = mapper.selectIds(ids);
userList.forEach(System.out::println);
}
<select id="selectIds" resultType="com.atguigu.mybatis.pojo.User">
select * from user where id in
<foreach collection="array" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
集合List
接收List集合用作sql查询
注意:
- int数组转化为List时,通过Arrays.asList方法转化
- 转化时如果用 int[ ] 则转化不成功, 因为在转化List时, 需要调用get和set方法 而 int 类型中没有get和set方法. 但是在包装类中 Integer 中有get和set方法
@Test
public void test02(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer[] ids = {1,2,4,5,6};
List<Integer> list = Arrays.asList(ids);
List<User> userList = mapper.selectIdsByList(list);
userList.forEach(System.out::println);
}
<select id="selectIdsByList" resultType="com.atguigu.mybatis.pojo.User">
select * from user where id in
<foreach collection="list" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
Map
接收Map进行sql查询
public void test03(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer[] ids = {1,2,4,5,6};
List<Integer> list = Arrays.asList(ids);
HashMap<Object, Object> map = new HashMap<>();
map.put("ids",list);
List<User> userList = mapper.selectIdsByMap(map);
userList.forEach(System.out::println);
}
注意:
- 遍历的是Map中的List 而不是 Map
- foreach标签中 collection中要写对应的 key
<select id="selectIdsByMap" resultType="com.atguigu.mybatis.pojo.User">
select * from user where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
动态sql
动态查询
查询时可能单单用某个字段的属性查询,而并不是所有字段都有属性进行查询
- 根据对象中不为null的属性,充当where条件
想要查询某条记录
@Test
public void test04(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(null, "大乔", null, "女");
List<User> userList = mapper.selectUser(user);
userList.forEach(System.out::println);
}
<if标签>动态拼接 如果id为空 sql语句报错
< where >可以去除 末尾多余的 1 个 and
< if >标签要和 < where >标签联合使用
<select id="selectUser" resultType="com.atguigu.mybatis.pojo.User">
select * from user
<where>
<if test="id != null">id = #{id}</if>
<if test="name != null and name != '' ">and name = #{name}</if>
<if test="age != null and age != 0">and age = #{age}</if>
<if test="sex != null and sex != '' ">and sex = #{sex}</if>
</where>
</select>
sql动态修改
当更新数据时, 只想修改某个字段, 则其他字段为空, 按照惯例修改, 则其他字段的null会赋值到字段中. 这时就需要判断哪些字段为空, 然后进行修改
只修改指定数据 例如 只修改 name 则 age 和 sex 为 null
//动态修改
@Test
public void test05(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(5, "华雄", null, null);
int rows = mapper.updateUser(user);
System.out.println("影响行数 = " + rows);
}
只用< if >标签判断会造成sql语法错误 会有多余的 “,” 号
< set > 标签动态删除 where 后的一个 “,” 号
<!--动态更新信息-->
<update id="updateUser">
update user
<set>
<if test="name != null and name != '' ">name = #{name},</if>
<if test="age != null and age != 0 ">age = #{age},</if>
<if test="sex != null and sex != '' ">sex = #{sex}</if>
</set>
where id = #{id}
</update>
Trim标签万能标签()
- Trim是万能标签, 可以构建任意类型的sql语句
- 需求: 使用trim标签实现 where 标签 的功能
- 标签属性说明
- prefix 在trim结构提前 添加的前缀
- prefixOverrides: 在trim标签之前将某些标签去除
- suffix: 在trim结构体之后添加的后缀
- suffixOverrides: 在 trim结构体之后删除的标签
insert增加标签(自己写的)
<!--trim标签-->
<insert id="insertByTrim">
insert into user values
<trim prefix="(" suffix=")">
#{id},#{name},#{age},#{sex}
</trim>
</insert>
//Trim标签
@Test
public void test06(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(null, "姿儿态", 25, "男");
int rows = mapper.insertByTrim(user);
System.out.println("影响行数 = " + rows);
}
分支结构
问题展现
- 查询结果, 如果用户传入主键 id 主键,则根据主键查询
- 如果用户没有传递主键 id 传入的是其他字段, 则需要进行判断 先判断是否传入主键 id 然后继续判断传入的自他字段
这就是分支结构产生的原因
java代码
//分支结构
@Test
public void test07(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(null, "诸葛亮", 25, "男");
List<User> userList = mapper.selectChoose(user);
userList.forEach(System.out::println);
}
xml代码
<!--分支结构-->
<!--
第一个when表示if
其他when表示else-if
otherwise表示else
-->
<select id="selectChoose" resultType="com.atguigu.mybatis.pojo.User">
select * from user
<where>
<choose>
<when test="id != null">id = #{id}</when>
<when test="name != null">name = #{name}</when>
<when test="age != null">age = #{age}</when>
<otherwise>sex = #{sex}</otherwise>
</choose>
</where>
</select>
动态sql作业
- 批量新增
- 准备多个对象,使用一条sql
注意: #{ } 内要写 对象.属性名
而不是直接写 属性名
结果集封装 - resultMap(重点用法)
问题展现: 查询 id ,name ,age ,sex 之后,想用 user_id, user_name, user_age, user_sex 来接收 . 但对象属性与字段不匹配. 输出结果都为null
<!--
<id>表示主键字段
column表示对应表中的字段
property表示对应对象中的属性
-->
<resultMap id="userMap" type="User">
<!--主键字段-->
<id column="user_id" property="id"></id>
<!--其他字段-->
<result column="user_name" property="name"></result>
<result column="user_age" property="age"></result>
<result column="user_sex" property="sex"></result>
</resultMap>
//resultMap用法
@Test
public void test01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList= mapper.selectResult();
userList.forEach(System.out::println);
}
补充:
<id column="user_id" property="id" javaType="Integer" jdbcType="INTEGER"></id>
可以用 javaType
和 jdbcType
设置强制转换
关于Mybatis中驼峰映射说明
- 数据库中的字段与对象中的属性, 满足驼峰命名规则
user_id -> userId
将结果的字段按驼峰命名法直接赋值给对象 前提是对象中的属性是驼峰命名
在mybatis配置中的说明
setting name属性可以在官网找到
注意: setting要写在propertyis后
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
映射文件中的代码
<select id="selectResult2" resultType="Person">
select id user_id,name user_name,age user_age,sex user_age from user
</select>
java代码
//驼峰命名法则
@Test
public void test02(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Person> userList= mapper.selectResult2();
userList.forEach(System.out::println);
}
注解开发
@Select 写在接口方法上
public interface EmpMapper {
//在方法上面添加注释
@Select("select * from emp")
List<Emp> selectList();
}
关联关系
关联关系介绍
- 一对一
- 一对多
- 多对多
一对一的关联
编辑实体对象
基本操作
利用注解开发
查询结果缺少部门信息
多表关联查询
一对一
一对一
sql语句
select emp.*,dept.dept_name from emp,dept where emp.dept_id = dept.dept_id
select
*
from
emp
RIGHT JOIN
dept
ON
emp.dept_id = dept.dept_id
在进行数据封装时,enp 中一个属性为一个对象, 无法用正常的数据类型进行封装
这时就用到一个标签
< association ></ association> 标签用于属性是一个对象时
<select id="selectList" resultMap="empRM">
select emp.*,dept.dept_name from emp,dept where emp.dept_id = dept.dept_id
</select>
<resultMap id="empRM" type="com.atguigu.mybatis.pojo.Emp">
<id property="empId" column="emp_id"></id>
<result property="empName" column="emp_name"></result>
<result property="empAge" column="emp_age"></result>
<association property="dept" javaType="Dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
满足驼峰命名法则 , 则可以省略
省略之后的输出结果
将< association >打开后, 将可以使用驼峰命名的语句注释掉
输出结果
关羽自动映射的说明:
1.如果没有其他关联数据(单表操作), 则满足驼峰命名规则时, 可以自动封装
2.如果是关联操作,则需手动配置 映射规则 则需要使用属性 autoMapping=“true”
3.autoMapping只对当前对象有效
打开autoMapping
xml代码
<!--
关羽自动映射的说明:
1.如果没有其他关联数据(单表操作), 则满足驼峰命名规则时, 可以自动封装
2.如果是关联操作,则需手动配置 映射规则 则需要使用属性 autoMapping="true"
3.autoMapping只对当前对象有效
-->
<select id="selectList" resultMap="empRM">
select emp.*,dept.dept_name from emp,dept where emp.dept_id = dept.dept_id
</select>
<resultMap id="empRM" type="com.atguigu.mybatis.pojo.Emp" autoMapping="true">
<id property="empId" column="emp_id"></id>
<!--<result property="empName" column="emp_name"></result>
<result property="empAge" column="emp_age"></result>-->
<association property="dept" javaType="Dept" autoMapping="true">
<id property="deptId" column="dept_id"></id>
<!-- <result property="deptName" column="dept_name"></result>-->
</association>
</resultMap>
java测试代码
@Test
public void testOneToOne(){
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> empList = mapper.selectList();
/*empList.forEach(emp ->{
System.out.println("员工信息:" + emp.getEmpName() + "~~~ 部门名称:" + emp.getDept().getDeptName());
});*/
empList.forEach(System.out::println);
}
一对多
sql语句
select dept.dept_name,emp.* from emp,dept where dept.dept_id = emp.dept_id
Dept类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private Integer deptId;
private String deptName;
private List<Emp> emps;
}
测试代码
//一对多
@Test
public void testOneToMore(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> deptList = mapper.selectList();
deptList.forEach(System.out::println);
}
xml文件代码
<select id="selectList" resultMap="deptMap" >
select dept.dept_name,emp.* from emp,dept where dept.dept_id = emp.dept_id
</select>
<resultMap id="deptMap" type="Dept" autoMapping="true">
<id column="dept_name" property="deptName"></id>
<collection property="emps" ofType="Emp" autoMapping="true">
<id property="empId" column="emp_id"></id>
</collection>
</resultMap>
< collection property=“emps” ofType=“Emp” autoMapping=“true”>< id property=“empId” column=“emp_id”></ id> </ collection>
作业
- 完成老师和学生的多对多查询
子查询介绍
使用多次查询查询, 而不是一次的联表查询
select * from dept
select * from emp where emp_id = ?
使用子查询优化sql
说明:如果数据量过大进行关联查询时性能低,所以将多表关联查询转化为多张单表查询.
运行结果中显示执行4条sql语句:
java测试代码
//一对多子查询方法
@Test
public void testOneToMoreChildQuery(){
SqlSession sqlSession = sqlSessionFactory.openSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> deptList = mapper.selectListByChildQuery();
deptList.forEach(System.out::println);
}
xml映射文件中的配置
<!--一对多子查询-->
<select id="selectListByChildQuery" resultMap="deptMap2">
select * from dept
</select>
<resultMap id="deptMap2" type="Dept" autoMapping="true">
<id column="dept_id" property="deptId"></id>
<collection property="emps" ofType="Emp" select="empSelect" column="dept_id" autoMapping="true">
<id column="emp_id" property="empId"></id>
</collection>
</resultMap>
<select id="empSelect" resultType="Emp">
select * from emp where #{dept_id}
</select>
使用 < collection >< / collection >标签中的 select , column 标签来封装List
懒加载机制
默认是饿汉式加载
需要用到哪几个数据就查询哪几个数据, 而不是无论什么情况都进行全查找
如果全部数据都要使用则, 按照默认使用饿汉式加载
在< collection >< /collection >标签中添加fetchType属性来修改加载机制
可在mybatis配置文件中, 添加配置设置全局懒加载机制
<!--添加mybatis的设置-->
<settings>
<!--开启了驼峰映射规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--设置全局懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
课堂案例
- 查询所有员工的信息以及所属部门的名称。
- 查询年龄大于等于30岁的员工信息以及所属部门的名称。
- 查询姓名为"张三"的员工信息以及所属部门的名称。
- 查询部门名称为"技术部"的员工信息。
- 查询年龄在25到35岁之间的员工信息以及所属部门的名称。
- 查询部门名称包含"销售"字样的员工信息。
- 查询员工数量以及每个部门的员工人数。
- 查询按照年龄降序排列的员工信息以及所属部门的名称。
- 查询姓名包含"李"字的员工信息以及所属部门的名称。
- 查询年龄最大的员工信息以及所属部门的名称。