MyBatis(第2天)-基本使用
项目结构
-
mybatis_day02_01_xml用户的增删改查操作
-
mybatis_day02_02_old传统DAO实现类方式
-
mybatis_day02_03_anno注解方式
-
mybatis_day02_04_dynamic_sql动态SQL
mybatis框架的学习,不要纠结,不要老想wsm,我们的目标使用mybatis,等我们框架使用熟练后,看源码学习.
三种开发方式
- mapper接口代理的开发方式(重点)
- 使用注解的开发方式
- 传统DAO实现类的开发方式(目前基本淘汰)
接口代理方式:搭建项目环境
目标
搭建Mybatis项目环境
[外链图片转存失败(img-Zc8ozGUQ-1565188451528)(/1562577845493.png)]
数据库SQL语句
CREATE TABLE USER (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20) NOT NULL,
birthday DATE,
sex CHAR(1) DEFAULT '男',
address VARCHAR(50)
);
INSERT INTO USER VALUES (NULL, '孙悟空','1980-10-24','男','花果山水帘洞');
INSERT INTO USER VALUES (NULL, '白骨精','1992-11-12','女','白虎岭白骨洞');
INSERT INTO USER VALUES (NULL, '猪八戒','1983-05-20','男','福临山云栈洞');
INSERT INTO USER VALUES (NULL, '蜘蛛精','1995-03-22','女','盤丝洞');
实现步骤
- 创建模块,加入依赖包
- 复制核心配置文件sqlMapConfig.xml和log4j.properties到src下
创建模块,加入依赖包
准备核心配置文件
sqlMapConfig.xml和log4j.properties
<?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>
<!--可以配置多个环境,可以访问不同种类的数据库:mysql, oracle-->
<environments default="default">
<!-- 其中的一个配置环境,这个配置方案的唯一标识 -->
<environment id="default">
<!--
指定事务管理器的类型:
JDBC:使用JDBC来管理事务
-->
<transactionManager type="JDBC"/>
<!--
数据源的类型:
1. POOLED:使用mybatis创建的连接池
2. UNPOOLED:不使用连接池,每次都创建和关闭连接
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--加载接口映射文件-->
<mappers>
</mappers>
</configuration>
小结
搭建mybatis项目步骤
- 创建模块
- 导入jar包
- 复制核心配置文件到src下
- 修改数据库的参数
接口代理方式:根据用户ID查询用户
目标
根据用户 ID 查询用户
步骤分析
- 编写用户User的实体类
- 编写用户UserMapper接口
- 配置接口映射文件UserMapper.xml
- 使用SqlSession来执行Mapper的方法
编写用户User的实体类
package com.itheima.entity;
import java.sql.Date;
/**
用户实体类对象 */
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 省略构造方法/getter/setter/toString
}
编写用户UserMapper接口
public interface UserMapper {
/*
通过id查询到一个用户
*/
User findUserById(Integer id);
}
创建UserMapper.xml文件
在com.itheima.dao包下创建UserMapper.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="com.itheima.dao.UserMapper">
<!--parameterType:方法参数的类型-->
<!--resultType:方法返回值类型-->
<!--#{}:相当于?占位符,#{id}:得到方法的id参数放在这个位置-->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.itheima.entity.User">
SELECT * FROM USER WHERE id=#{id};
</select>
</mapper>
在mapper中配置上面的UserMapper.xml文件
<!--配置接口映射文件-->
<mappers>
<mapper resource="com/itheima/dao/UserMapper.xml"/>
</mappers>
测试
public class TestUserMapper {
@Test
public void test01() throws IOException {
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(in);
SqlSession sqlSession = sessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userById = mapper.findUserById(2);
System.out.println("userById = " + userById);
}
}
小结
- 如何取到方法的参数放到sql中
#{id}: 先使用?占位,再取出方法的id这个参数,放到这个位置
核心配置文件:properties标签
目前我们将数据库相关的信息都是配置在核心配置文件中,如果核心配置文件中的内容很多,那么修改的时候就不是很方便。我们可以将数据库的配置信息专门放到一个单独的文件中,使用properties标签引入即可。
目标
学习properties标签的作用
sqlMapConfig.xml元素概述
sqlMapConfig.xml是mybatis框架的核心配置文件,目前我们在其中配置了运行环境(数据源)和加载映射文件。该配置文件中还有其它的一些配置。
properties的作用
将外面的属性文件(.properties)加载进来。在后面就可以引用属性文件中的键和值
操作步骤
编写数据库连接属性资源文件(jdbc.properties)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
在核心配置文件中通过properties标签加载jdbc.properties属性资源文件
<!--
属性:
resource: 指定类路径下Java的属性文件
url: 在指定的地址上得到一个Java的属性文件。 协议名://地址/资源名.
<properties url="http://www.itheima.com/db.properties"/>
如果内部和外部有相同的键,使用外部的
-->
<properties resource="db.properties">
<property name="jdbc.password" value="root"/>
</properties>
小结
-
properties作用是什么?
引入外部.properties文件.
-
内部配置的property和外面配置的properties如果同名,使用哪个?
使用外部的
核心配置文件:typeAliases别名
我们在接口映射文件中指定参数或返回值类型时,需要写出具体的包名和类名,是比较麻烦的。我们可以通过别名更简单,比如中华人民共和国,简称中国。
目标
学习typeAliases标签的使用
typeAliases作用
typeAliases别名,简化类型的书写。
内置别名
别名 | 映射类型 |
---|---|
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
说明:
-
内置别名可以直接使用
-
别名不区分大小写
自定义类取别名方式一:typeAlias
- 在配置文件中将用户指定别名为user
- 将结果类型设置为user
- 省略alias的情况
<typeAlias type="com.itheima.entity.User" alias="User"/>
<typeAlias type="com.itheima.entity.User"/>
自定义类取别名方式二:包扫描配置别名package
如果有很多类,那么都需要取别名,一个一个取别名太麻烦了。可以使用包扫描方式。
<!--
定义别名
typeAlias 子元素
type: 类全名
alias: 别名,可以省略。默认使用类名做为别名,不区分大小写
package子元素:
给包里面所有类取别名, 别名就是类名,不区分大小写
-->
<typeAliases>
<package name="com.itheima.entity"/>
</typeAliases>
小结
typeAlias标签的作用:
给类取别名
package标签的作用:
包扫描,将这个包里面所有的类都取别名,别名就是类名
核心配置文件:mappers(映射器)
目标
学习mappers标签的使用
作用
作用:加载接口映射文件
方式一:加载单个映射文件
mapper标签的属性
<!--映射器-->
<mappers>
<!--
resource: 指定类路径下映射文件,注:路径使用/做为分隔符,而不是点号
class: 指定使用注解的接口名字
-->
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
注:如果是多级目录,是/而不是点号
方式二:包扫描加载多个映射文件
包扫描方式加载mapper映射文件
-
要求接口映射文件,与接口要放在同一个目录
-
要求接口映射文件的名称,与接口的名称要一致
<!--配置接口映射文件-->
<mappers>
<!--
package标签:
接口映射文件与接口文件在同一个目录
接口映射文件的名称与接口文件的名称相同
-->
<package name="com.itheima.dao"/>
</mappers>
小结
properties作用?
引入外部.properties文件,可以将一部分的配置信息放到另外的文件
typeAliases作用?
给类取别名,使用简单,建议使用包扫描的方式
mappers作用?
配置接口映射文件的位置,推荐使用包扫描
接口代理方式:新增用户
目标
- 添加用户
- 学习mybatis处理事务
效果
实现步骤
- 在mapper接口定义新增用户方法
- 配置mapper映射文件
- 测试
在mapper接口定义新增用户方法
/**
* 新增用户
*/
void addUser(User user);
配置mapper映射文件
- 新增用户使用insert标签
- 放置新增sql语句,参数类型使用User
- 占位符使用user对象的各个#{属性名}
<!--
insert标签:表示添加记录
id: 方法名
如果参数是实体类,#{属性名}
-->
<insert id="addUser" parameterType="user">
INSERT INTO user VALUES (null, #{username}, #{birthday},#{sex},#{address})
</insert>
测试
new User(null,"白龙马",Date.valueOf("2019-05-01"),"男","东海龙宫");
- 事务的处理:如果Java程序代码执行成功,但是数据库中并没有新增记录。原因是没有提交事务,在对数据库的更新操作中(增、删、改)要求提交事务。
提交事务
方式一:手动提交事务
sqlSession.commit(); // 方式一:手动提交事务
方式二:自动提交事务
sqlSession = factory.openSession(true); // 方式二:自动提交
说明:如果在同一个方法中,有多个数据库操作,需要使用手动提交的方式。
小结
添加一条记录,步骤:
- 在接口中新增一个方法
-
在接口映射文件中添加SQL语句
-
参数是实体对象的时候,占位符#{}中的变量名是什么?
写对象的成员变量名
-
作开启事务有哪两种方式?
手动提交: sqlSession.commit();
自动提交: 在得到SqlSession的时候,传入参数:true
factory.openSession(true);
查询新增记录的主键值
上面的添加方法是返回影响的行数,如果要得到添加的主键值怎么操作?
说明:当数据库表中,主键字段值由数据库维护(比如mysql中的自增长),那么在新增完一条记录以后,如何获取到数据库维护的主键值呢?
目标
通过子元素<selectKey>
得到新增记录的主键值
通过子元素useGeneratedKeys
得到新增记录的主键值
需求效果
方式一:子元素<selectKey>(常用)
mysql中的函数:**last_insert_id() **:得到最后添加的主键
原理:在insert语句执行后再执行一条查询语句,返回新增主键的id
属性 | 说明 |
---|---|
keyColumn | 主键在表中的字段:user表中id列 |
keyProperty | 实体类中主键的属性名 User中id |
resultType | 主键类型,int类型 |
order | BEFORE: 在insert之前执行 AFTER: 在insert之后执行 |
<!--
insert标签:表示添加记录
id: 方法名
如果参数是实体类,#{属性名}
-->
<insert id="addUser2" parameterType="user">
<!--
keyColumn 主键在表中的字段:user表中id列
keyProperty 实体类中主键的属性名 User中id
resultType 主键类型,int类型
order
BEFORE: 在insert之前执行
AFTER: 在insert之后执行
-->
<selectKey keyColumn="id" keyProperty="id" resultType="int">
SELECT LAST_INSERT_ID();
</selectKey>
INSERT INTO user VALUES (null, #{username}, #{birthday},#{sex},#{address})
</insert>
测试代码
//添加记录
@Test
public void testAddUser() {
//在mybatis中默认是手动提交事务,增删改要提交事务
//Date.valueOf(字符串),将字符串转成日期,格式一定要是yyyy-MM-dd
User user = new User(null,"白龙马", Date.valueOf("2019-05-01"),"男","东海龙宫");
int row = userMapper.addUser(user);
System.out.println("user = " + user);
}
方式二:在insert标签中增加属性
属性 | 说明 |
---|---|
useGeneratedKeys | true,使用mysql生成的主键 |
keyColumn | 表中主键对应的字段 |
keyProperty | 实体类中对应的属性 |
映射文件
<!--
insert标签:表示添加记录
id: 方法名
如果参数是实体类,#{属性名}
-->
<insert id="addUser" parameterType="user" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
INSERT INTO user VALUES (null, #{username}, #{birthday},#{sex},#{address})
</insert>
- 说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库,比如MySQL或SQL Server。
小结
-
得到主键值有哪两种方式?
1. <selectKey>
2.在insert标签中添加 useGenerateKeys
接口代理方式:修改数据
目标
通过id修改用户数据
实现步骤
- 在接口中编写一个修改用户的方法
- 在接口映射文件中编写相应的SQL语句
- 测试
在接口中编写一个修改用户的方法
/**
* 根据用户Id修改用户
*/
void updateUser(User user);
在接口映射文件中编写相应的SQL语句
使用update标签:放置修改sql语句,根据用户id修改用户其它属性
<!--
update标签更新记录
id: 方法名字
parameterType: 参数的类型
-->
<update id="updateUser" parameterType="user">
UPDATE user set username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} where id=#{id}
</update>
测试
- 修改6号用户的名字,生日,性别,地址
- 更新用户对象
//更新记录
@Test
public void testUpdateUser() {
User user = new User(6, "狐狸精",Date.valueOf("1996-05-10"),"女","狐狸洞");
userMapper.updateUser(user);
}
小结
修改数据的步骤:
1.在接口定义一个修改的方法
2.在接口映射文件中添加对应SQL语句
3.测试
编写修改的SQL语句使用哪个标签?
update标签
接口代理方式:删除数据
目标
根据id删除数据
效果
实现步骤
- 在接口中编写一个删除用户的方法
- 在接口映射文件中编写相应的SQL语句
- 测试
在接口中编写一个删除用户的方法
/**
* 根据用户id删除用户
*/
void deleteUser(int id);
在接口映射文件中编写相应的SQL语句
delete标签:放置删除sql语句,根据用户Id删除用户
<!--
delete标签用于删除记录
-->
<delete id="deleteUser" parameterType="int">
DELETE from user where id=#{id}
</delete>
测试
删除11号用户
//删除记录
@Test
public void testDeleteUser() {
userMapper.deleteUser(11);
}
接口代理开发方式小结
-
mapper映射文件中namespace属性值写?接口的类全名
-
mapper映射文件
-
sql语句标签中的id属性值写?方法名
-
sql语句标签中的parameterType属性值写?方法参数类型
-
sql语句标签中resultType属性值写?方法返回值类型
如果方法参数或返回值是基本数据类型可以省略不写
-
mybatis模糊查询
目标
使用接口代理方式模糊查询用户
实现步骤
- 在接口中编写一个模糊查询的方法
- 在接口映射文件中编写相应的SQL语句
- 测试
方式一
在接口中编写一个模糊查询的方法
/**
通过用户名模糊查询用户
*/
List<User> findUsersByName(String username);
在接口映射文件中编写相应的SQL语句
SELECT * FROM user WHERE username LIKE '%精%';
<select id="findUserByname" parameterType="string" resultType="user">
SELECT * FROM user WHERE username LIKE #{name};
</select>
测试
查询名字中包含"精"字的用户
// 按名字模糊查询用户
@Test
public void testFindUsersByName() {
List<User> list = mapper.findUserByname("%精%");
for (User user : userList) {
System.out.println(user);
}
}
方式二
在接口映射文件中编写相应的SQL语句
<select id="findUserByname" parameterType="string" resultType="user">
SELECT * FROM user WHERE username LIKE '%${value}%';
</select>
测试2
查询名字中包含"精"字的用户
// 按名字模糊查询用户
@Test
public void testFindUsersByName() {
List<User> list = mapper.findUserByname("精");
for (User user : userList) {
System.out.println(user);
}
}
小结
接口映射文件:三种参数类型
目标
-
Java简单类型
-
POJO实体类型
-
POJO包装类型
简单类型
什么是简单类型:基本数据类型的8种+String,包括对应的包装类
POJO类型
POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBean,即我们前面封装数据使用的实体类
POJO包装类型
什么是POJO包装类型:就是在实体类中包含了其它的实体类
public class QueryVo {
private User user;
private String start;
private String end;
}
案例: POJO包装类型的演示
目标
实现多条件查询,出生日期在start属性和end属性之间
方法定义多个参数方式实现实现多条件查询
SELECT * FROM user WHERE birthday BETWEEN 值1 AND 值2;
接口
List<User> findUsersByBirthday(String start, String end);
接口映射文件
<select id="findUsersByBirthday" parameterType="string" resultType="user">
SELECT * FROM user WHERE birthday BETWEEN #{arg0} AND #{arg1};
</select>
测试
@Test
public void test10() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.findUsersByBirthday("1990-01-01", "2018-01-01");
for (User user : list) {
System.out.println("user = " + user);
}
sqlSession.close();
}
存在的问题,SQL语句可读性太差
POJO包装类型实现多条件查询
编写POJO包装类型
VO: Value Object 值对象,实体类
package com.itheima.entity;
/**
包装类
*/
public class QueryVo {
private User user; // 包含用户对象
private String start; // 开始日期
private String end; // 结束日期
// 省略getter/setter
}
声明mapper接口方法
/**
* 使用POJO包装类型,根据用户名称,开始和结束生日,模糊查询用户
*/
List<User> findUsersByCondition(QueryVo queryVo);
配置mapper映射文件
<!--
queryVo:要定义别名
占位符,拼接符要使用实体类的属性名
-->
<select id="findUsersByCondition" parameterType="queryvo" resultType="user">
SELECT * FROM user WHERE id < #{user.id} AND birthday BETWEEN #{start} AND #{end};
</select>
测试
@Test
public void testFindUsersByCondition() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
QueryVo vo = new QueryVo();
User u = new User(10, "老宋", Date.valueOf("2000-02-02"), "男", "东莞");
vo.setUser(u);
vo.setStart("1990-01-01");
vo.setEnd("2018-01-01");
List<User> list = mapper.findUsersByCondition(vo);
for (User user : list) {
System.out.println("user = " + user);
}
// 手动提交事务
sqlSession.commit();
sqlSession.close();
}
小结
三种参数输入类型分别是:
-
简单类型
8种基本数据类型/包装类 + String
-
POJO类型
简单的Java类型。POJO类型就是JavaBean
-
POJO包装类型
优点是方便传入多个参数。 public class QueryVo { private User user; // 包含用户对象 private String start; // 开始日期 private String end; // 结束日期 // 省略getter/setter }
接口映射文件:resultType输出类型
目标
学习输出结果resultType的两种类型
- 简单类型
- POJO类型
resultType的两种类型
java简单类型
POJO类型
java简单类型练习
统计用户表中女生的数量
声明mapper接口方法
/**
* 统计用户表中某种性别的数量
*/
int getAmountBySex(String sex);
配置mapper映射文件
<!-- 统计用户表中的女生的用户数量 -->
<select id="getAmountBySex" resultType="int" parameterType="string">
select count(*) from user where sex=#{sex}
</select>
测试
// 查询有多少个女生
@Test
public void testGetAmountBySex() {
int amount = userMapper.getAmountBySex("女");
System.out.println("女生数量是:" + amount);
}
小结
resultType的输出类型有哪两种?
-
POJO类型
-
简单类型
resultType是简单类型也不能省略
接口映射文件:resultMap输出映射
mybatis可以把查询的结果自动封装为对象。但是有要求:数据库中的列名称,要与对象的属性一致。否则不能正确封装数据。
当查询的列与对象属性不一致时候,使用resultMap解决。resultMap可以建立查询的列与对象属性的对应关系。
SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = 2;
查询一张表时,查询的字段名字和类的中的成员变量不一样,设置为null,名称一样的就有值
目标
使用resultMap对查询结果进行映射
配置mapper映射文件
- 定义resultMap标签
- id标签:映射主键字段,如果列名与属性名相同可以省略
- result标签:映射普通字段,指定哪个属性对应哪个列
- 在查询的结果中使用resultMap
<!--
定义结果映射
id: 映射的唯一标识
type: 最终结果转换后的类型
子元素:
id: 定义主键字段的映射
result: 定义普通的字段的映射
属性:property: 实体类中属性名,column: 表中列名,如果相同可以不写。
->
<resultMap id="u1" type="user">
<id column="id2" property="id"/>
<result column="username2" property="username"/>
<result column="birthday2" property="birthday"/>
</resultMap>
<select id="findUserByIdUseResultMap" parameterType="int" resultMap="u1">
SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = #{id};
</select>
测试,查询id为1的用户
@Test
public void test09() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserByIdUseResultMap(2);
System.out.println("user = " + user);
// 手动提交事务
sqlSession.close();
}
映射流程
映射文件的标签小结
<select>
用于执行查询的SQL
<insert>
用于执行插入的SQL
<update>
用于执行修改的SQL
<delete>
用于执行删除的SQL
<resultMap>
对查询的结果进行映射
传统的DAO实现类的开发方式[了解]
目标
使用传统的DAO实现类开发方式
- 查询id的用户
- 添加1条记录
复制新的项目
- 在 windows 下将 mybatis-day02-01-xml 复制成 mybatis-day02-02-old
- 在 windows 下将 mybatis-day02-01-xml.iml 的文件名改成 mybatis-day02-02-old.iml
- 导入mybatis-day02-02-old项目
编写UserMapper接口
public interface UserMapper {
/*
通过id查询到一个用户
*/
User findUserById(Integer id);
void addUser(User user);
}
编写UserMapper接口映射文件
<?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="com.itheima.dao.UserMapper">
<select id="findUserById" parameterType="int" resultType="uSer">
SELECT * FROM USER WHERE id=#{id};
</select>
<insert id="addUser" parameterType="user">
INSERT INTO user (username, birthday, sex, address) VALUES (#{username}, #{birthday}, #{sex}, #{address});
</insert>
</mapper>
编写接口实现类UserMapperImpl
package com.itheima.dao;
import com.itheima.entity.User;
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;
public class UserMapperImpl implements UserMapper {
private static SqlSessionFactory factory;
static {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(UserMapperImpl.class.getResourceAsStream("/sqlMapConfig"));
}
@Override
public User findUserById(Integer id) {
SqlSession session = factory.openSession();
User user = session.selectOne("com.itheima.dao.UserMapper.findUserById", id);
session.close();
return user;
}
@Override
public int addUser(User user) {
SqlSession session = factory.openSession();
int insert = session.insert("com.itheima.dao.UserMapper.addUser", user);
session.commit();
session.close();
return insert;
}
}
编写测试类
public class TestUserMapper {
@Test
public void test01() {
UserMapperImpl dao = new UserMapperImpl();
User user = dao.findUserById(7);
System.out.println("user = " + user);
}
@Test
public void test02() {
UserMapperImpl dao = new UserMapperImpl();
User u = new User(10, "小王", Date.valueOf("2022-02-02"), "男", "深圳");
dao.addUser(u);
}
}
小结
传统的DAO实现类的开发方式和接口实现类的方式区别?
DAO实现类多需要接口的实现类,在实现类中找到SQL语句执行.
注解开发方式:搭建项目环境
目标
搭建注解项目环境
新建项目
项目名为:mybatis02_03_anno_crud
<!--我们没有编写UserMapper.xml,会扫描UserMapper接口,得到接口里面的注解和方法也能生成代理对象-->
<mappers>
<package name="com.itheima.dao"/>
</mappers>
注解开发方式:查询
目标
通过注解方式实现:根据用户id查询用户
编写UserMapper接口,在方法上配置注解
public interface UserMapper {
/*
通过id查询1个用户
会执行方法上面的注解里面的SQL语句
Select表示执行查询的SQL
*/
@Select("SELECT * FROM user WHERE id=#{id};")
User findUserById(Integer id);
}
编写测试类
// 查询1个用户对象
@Test
public void testFindUserById() throws IOException {
SqlSession sqlSession = sessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserById(4);
System.out.println("user = " + user);
}
小结
注解方式操作Mybatis步骤
-
在接口中定义操作数据库的方法
-
在方法上配置注解:@Select(“SQL语句”)
-
测试
使用@Results和@Result属性
需求变化
通过注解方式实现:根据id查询user表中一条记录,对结果封装
注解说明
注解 | 属性 | 说明 |
---|---|---|
@Results | 相当于resultMap表示要对结果进行映射 | |
@Result | 对一个字段进行映射 | |
column | 查询的字段名 | |
property | 类中的成员变量名 | |
id | true 表示是主键 |
<resultMap id="u1" type="user">
<id column="id2" property="id"/>
<result column="username2" property="username"/>
<result column="birthday2" property="birthday"/>
</resultMap>
<select id="findUserByIdUseResultMap" parameterType="int" resultType="user">
SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = #{id};
</select>
UserMapper接口代码
// 查询的SQL语句
@Select("SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = #{id};")
// 将结果集封装成对象
@Results({
// 一个字段转成一个对象的属性
@Result(id = true, column = "id2", property = "id"),
@Result(column = "username2", property = "username"),
@Result(column = "birthday2", property = "birthday")
})
User findUserById2(Integer id);
小结
@Select注解作用?
存放查询的SQL语句
@Results注解作用?
表示对查询的结果进行映射
@Result注解作用?
对一个字段进行映射
注解开发方式:修改和删除
目标
通过注解方式实现:
-
根据用户id修改用户
-
根据用户id删除用户
-
添加新的用户
根据用户id修改用户(@Update)
UserMapper接口:写修改方法,在方法上使用注解@Update(“SQL语句”)
/*
根据用户Id修改用户
*/
@Update("UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} WHERE id=#{id}")
void updateUser(User user);
测试
@Test
public void testupdateUser() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User u = new User(12, "老宋222", Date.valueOf("2000-02-02"), "男", "东莞");
mapper.updateUser(u);
sqlSession.commit();
}
根据用户id删除用户(@Delete)
UserMapper接口:使用注解,编写删除方法@Delete(“SQL”)
/*
根据用户id删除用户
*/
@Delete("DELETE FROM user WHERE id=#{id}")
void deleteUser(Integer id);
测试
@Test
public void testDeleteUser() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 根据用户id删除用户
mapper.deleteUser(11);
sqlSession.commit();
}
新增用户(@Insert)
UserMapper接口:使用注解,编写新增用户方法@Insert(“SQL语句”)
/*
新增用户
*/
@Insert("INSERT INTO user VALUES (NULL, #{username}, #{birthday}, #{sex}, #{address});")
void addUser(User user);
测试代码
@Test
public void testInsertUser() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User u = new User(0, "老刘", Date.valueOf("2012-02-02"), "女", "东莞");
// 添加记录
mapper.addUser(u);
sqlSession.commit();
}
小结
注解开发的步骤
- 在接口中定义方法
- 在方法头上配置相应注解,写SQL语句
获取新增主键值@SelectKey
目标
使用注解方式获取添加数据后的主键
<insert id="addUser2" parameterType="user">
INSERT INTO user (username, birthday, sex, address) VALUES (#{username}, #{birthday}, #{sex}, #{address});
<selectKey resultType="int" keyColumn="id" keyProperty="id">
SELECT last_insert_id();
</selectKey>
</insert>
@SelectKey说明
属性 | 说明 |
---|---|
statement | 要执行的SQL语句:select last_insert_id() |
keyProperty | 实体类中主键的属性 |
keyColumn | 表中主键的列名 |
resultType | 主键的数据类型 |
before | false 表示after,true表示before |
UserMapper接口
/*
新增用户
*/
@Insert("INSERT INTO user VALUES (NULL, #{username}, #{birthday}, #{sex}, #{address});")
@SelectKey(statement = "SELECT LAST_INSERT_ID();", keyColumn = "id", keyProperty = "id", resultType = int.class, before = false)
void addUser(User user);
测试
// 添加记录
public void testInsertUser() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User u = new User(0, "老刘", Date.valueOf("2012-02-02"), "女", "东莞");
mapper.addUser(u);
System.out.println("新增后u = " + u);
sqlSession.commit();
}
Mybatis注解小结
在注解方式实现基本CRUD操作中,使用的注解有:
注解 | 描述 |
---|---|
@Select | p配置查询的SQL语句 |
@Results | 对查询结果映射 |
@Result | 对每个字段进行映射 |
@Update | 配置修改的SQL语句 |
@Delete | 配置删除的SQL语句 |
@Insert | 配置插入的SQL语句 |
@SelectKey | 得到添加的数据的主键 |
动态SQL的概念和环境搭建
之前我们编写SQL语句的时候都是将SQL语句固定写好的
SELECT * FROM product WHERE brand='小米' AND type='ai' AND size=55;
UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} WHERE id=#{id};
目标
学习动态SQL的概念
搭建动态SQL的环境
什么是动态SQL
动态SQL指的是:在程序运行时,根据传入的参数情况,拼接最终执行的sql语句。
搭建动态SQL的环境
模块名:mybatis_day02_04_dynamic_sql
配置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="jdbc.properties"/>
<typeAliases>
<package name="com.itheima.entity"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="dirver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.itheima.dao"/>
</mappers>
</configuration>
测试
public class TestApp {
// 省略其他sqlSession的创建
@Test
public void testFindUserBySex() {
List<User> list = userMapper.findUserByNameAndSex(new QueryVo("%王%", "女"));
for (User user : list) {
System.out.println("user = " + user);
}
}
}
小结
- 搭建mybatis环境
动态SQL:if标签
目标
用户名和性别查询用户
学习动态SQL语句:if标签的使用
if标签的格式
<if test="条件">
SQL语句
</if>
if标签的作用
如果满足条件,就会拼接这个SQL。
需求实现
定义QueryVo实体类
public class QueryVo {
private String username;
private String sex;
// 省略构造方法/getter/setter
}
声明UserMapper接口方法
package com.itheima.dao;
import com.itheima.entity.User;
import java.util.List;
public interface UserMapper {
/*
根据用户名称和性别查询用户
*/
List<User> findUserByNameAndSex(QueryVo vo);
}
配置mappr映射文件
<?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="com.itheima.dao.UserMapper">
<select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
SELECT * FROM user WHERE username LIKE #{username} AND sex=#{sex};
</select>
</mapper>
UserMapper.xml
- if:判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
- if:判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
<?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="com.itheima.dao.UserMapper">
<!--直接根据用户名和性别查询-->
<!-- <select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
SELECT * FROM user WHERE username LIKE #{username} AND sex=#{sex};
</select>-->
<!--
判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
-->
<select id="findUserByNameAndSex" parameterType="User" resultType="User">
select * from user where
<if test="username!=null and username!=''">
username like '%${username}%'
</if>
<!-- &&表示与操作,要转义 -->
<if test="sex!=null && sex!=''">
and sex = #{sex}
</if>
</select>
</mapper>
测试
- 通过用户名和性别查询多个用户
- 只设置性别
- 名字和性别一个都不设置
@Test
public void testFindUserBySex() {
List<User> list = userMapper.findUserByNameAndSex(new QueryVo("", ""));
for (User user : list) {
System.out.println("user = " + user);
}
}
疑问:if标签如果第1个条件没有,会出现什么情况?如何解决这个问题?
因为SQL语句拼接语句不正确,出现问题。
小结
动态SQL中if标签
if标签的含义做判断如果条件为true就拼接SQL
if标签的格式:
<if test"条件">
SQL
</if>
动态SQL:where标签的作用
目标
学习where标签的使用
where标签作用
- 相当于where关键字,自动补全where这个关键字
- 去掉多余的and和or关键字
需求实现
UserMapper.xml
if标签写在where标签内部
- if:判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
- if:判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
<select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
SELECT * FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE #{username}
</if>
<!--&&表示与操作,要转义-->
<if test="sex != null && sex != ''">
AND sex=#{sex};
</if>
</where>
</select>
小结
-
if标签的作用:
做判断,如果满足条件就拼接SQL
-
where标签的作用
- 替代WHERE关键字
- 如果有条件会自动添加WHERE关键字
- 如果没有条件就不添加WHERE关键字
- 去掉多余的AND OR
动态SQL:set标签
目标
set标签的使用
编写修改SQL语句存在的问题
之前我们编写修改的SQL语句时是根据用户ID更新用户所有字段的数据
<!--根据用户ID更新用户所有字段的数据, 这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新-->
<update id="updateUser" parameterType="user">
UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address}
WHERE id=#{id};
</update>
更新数据的时候,有些为空则不用更新,怎么来处理呢?
set标签的作用
- 用在update语句中,相当于set关键字
- 去掉SQL代码片段中后面多余的逗号
需求
根据id修改用户部分数据
实现
声明mapper接口方法
/**
更新用户
*/
int updateUser(User user);
配置mapper映射文件:根据id修改用户部分数据
<!--这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新-->
<update id="updateUser" parameterType="user">
UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} WHERE id=#{id};
</update>
测试
@Test
public void testUpdateUser() throws IOException {
User u = new User();
u.setId(26);
u.setUsername("大王");
userMapper.updateUser(u);
}
这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新。
使用set标签进行判断,如果有值就更新,没有值就不更新。
<update id="updateUser" parameterType="user">
UPDATE user
<set>
<if test="username != null and username != ''">
username=#{username},
</if>
<if test="birthday != null and birthday != ''">
birthday=#{birthday},
</if>
<if test="sex != null and sex != ''">
sex=#{sex},
</if>
<if test="address != null and address != ''">
address=#{address}
</if>
</set>
WHERE id=#{id};
</update>
set标签,可以按照条件拼接set后面的内容
小结
set标签的作用是?
用于更新,相当于set关键字,去掉多余的逗号
foreach标签:遍历集合1
MyBatis得到数组中的数据动态拼接SQL
int row = userMapper.deleteUsers(new int[]{1, 3, 6});
delete from user where id in (1, 3, 6);
使用数组来封装要删除的所有的id
目标
foreach标签:遍历集合得到基本类型的数据
需求
遍历集合拼接SQL语句,实现批量删除用户
foreach标签介绍
foreach标签的属性 | 作用 |
---|---|
collection | 要遍历的集合,2个取值:list或array |
item | 设置变量名,代表每个遍历的元素 |
separator | 每次遍历完后添加一个分隔符 |
#{变量名.属性} | 来引用每个属性中值 |
实现
声明mapper接口方法
/**
批量删除用户
*/
int deleteUsers(int[] ids);
配置mapper映射文件
<delete id="deleteUsers" parameterType="list">
<!--DELETE FROM user WHERE id in (1, 2, 3);-->
DELETE FROM user WHERE
<!--
collection: 取值为array表示遍历的是数组
open: 遍历前添加的符号
close: 遍历最后添加的符号
separator:分隔符
item: 表示每个元素的变量名
-->
<foreach collection="array" open="id in (" close=");" separator="," item="temp">
#{temp}
</foreach>
</delete>
测试
@Test
public void testDeleteUser() throws IOException {
int row = userMapper.deleteUsers(new int[]{24, 25, 26});
System.out.println("row = " + row);
}
效果
小结
foreach:遍历数组
- collection:取值为array
- open:开始的符号
- close:结束的符号
- item:每个元素变量名
- separator:分隔符
foreach标签:遍历集合2
目标
foreach标签:遍历集合得到自定义类型的数据
需求
使用list集合保存多个User对象,添加到数据库中
提问:一条insert语句插入多条记录的MySQL语句如何编写?
-- 同时插入多条记录
insert into user (username,birthday,sex,address) values
(#{username},#{birthday},#{sex},#{address}),
(#{username},#{birthday},#{sex},#{address}),
(#{username},#{birthday},#{sex},#{address});
/*
(#{username},#{birthday},#{sex},#{address}) 要循环的内容
*/
实现
mapper接口批量添加用户的方法
/**
批量添加用户
*/
int addUsers(List<User> users);
配置mapper映射文件
批量新增用户,参数类型是:list
<!--批量添加多条记录-->
<insert id="addUsers" parameterType="list">
insert into user (username,birthday,sex,address) values
<!--
collection 要遍历的集合使用list
item 设置变量名,代表每个遍历的元素
separator 每次遍历完后添加一个分隔符
#{变量名.属性} 来引用每个属性中值
-->
<foreach collection="list" item="user" separator=",">
(#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
测试
@Test
public void testAddUsers() throws IOException {
List<User> users = new ArrayList<>();
users.add(new User(null,"牛魔王", Date.valueOf("1980-01-30"),"男","火焰山"));
users.add(new User(null,"红孩儿", Date.valueOf("2009-05-08"),"男","火云洞"));
users.add(new User(null,"玉面狐狸", Date.valueOf("2005-11-01"),"女","狐狸洞"));
int row = userMapper.addUsers(users);
System.out.println("添加数据影响的行数 = " + row);
}
结果
小结
foreach:循环添加1个代码片段
- collection:2个取值list或array
- item:每次循环变量名
- separator:每次循环添加的分隔符
- 占位符: #{变量名.属性名}
sql和include标签
我们发现在接口映射文件中会出现很多相同的SQL语句,每个地方都写一遍有些麻烦。我们可以把相同的SQL语句抽取出来,在需要的地方引入即可。
目标
学习sql和include标签的使用
sql和include标签的作用
- sql标签:定义一段SQL语句,起个名字可以重用。
- include标签:引入上面定义的SQL代码段。
需求实现
UserMapper.xml
<!--抽取重复的SQL并取个名字-->
<sql id="commont">
INSERT INTO user (username,birthday,sex,address) VALUES
</sql>
<insert id="addUsers" parameterType="list">
<!--INSERT INTO user (username,birthday,sex,address) VALUES-->
<!--引入上面的的SQL-->
<include refid="commont"/>
<foreach collection="list" close=";" separator="," item="user">
(#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
小结
-
sql标签的作用:定义代码片段
id属性:唯一标识
-
include标签的作用:引用代码片段
refid属性:引用上面标识
学习总结
-
掌握sqlMapConfig.xml中常用标签
properties:引入外部的properties文件
typeAliases: 给类取别名,常用包扫描,别名就是类名
mappers: 配置接口映射文件的位置,常用包扫描。注意,接口和接口映射文件在同一个包,名字要相同
-
掌握mybatis框架在DAO层的开发
增删改查
3步走:
1.在接口中定义方法
2.在接口映射文件中配置SQL语句
3.测试
-
能够完成单表的CRUD操作
<insert> 表示插入数据 <select> 表示查询 <update> 表示修改 <delete> 表示删除 参数类型 返回值类型
-
掌握mybatis框架的输入输出映射
输入类型映射 简单类型: 基本类型8种/包装类 + String POJO: 简单的Java对象, JavaBean POJO包装类: class QueryVo { private User user; private String start; private String end; } 输出映射类型 简单类型: 基本类型8种/包装类 + String POJO: 简单的Java对象, JavaBean
-
掌握mybatis注解开发
2步骤
1.在接口种定义方法
2.在接口的方法上配置SQL语句
@Insert 表示插入数据 @Select 表示查询 @Update 表示修改 @Delete 表示删除
-
掌握 MyBatis 动态 sql 常用标签
<if test=”条件“> SQL </if> 满足条件就会拼接这个SQL <where> <if test=”条件“> </if> </where> 如果有条件就添加WHERE关键字 如果没有条件就不添加WHERE关键字 自动去掉多余的 and or
,“火焰山”));
users.add(new User(null,“红孩儿”, Date.valueOf(“2009-05-08”),“男”,“火云洞”));
users.add(new User(null,“玉面狐狸”, Date.valueOf(“2005-11-01”),“女”,“狐狸洞”));
int row = userMapper.addUsers(users);
System.out.println("添加数据影响的行数 = " + row);
}
### 小结
foreach:循环添加1个代码片段
- collection:2个取值list或array
- item:每次循环变量名
- separator:每次循环添加的分隔符
- 占位符: #{变量名.属性名}
## sql和include标签
我们发现在接口映射文件中会出现很多相同的SQL语句,每个地方都写一遍有些麻烦。我们可以把相同的SQL语句抽取出来,在需要的地方引入即可。
### 目标
学习sql和include标签的使用
#### sql和include标签的作用
1. sql标签:定义一段SQL语句,起个名字可以重用。
2. include标签:引入上面定义的SQL代码段。
#### 需求实现
UserMapper.xml
```xml
<!--抽取重复的SQL并取个名字-->
<sql id="commont">
INSERT INTO user (username,birthday,sex,address) VALUES
</sql>
<insert id="addUsers" parameterType="list">
<!--INSERT INTO user (username,birthday,sex,address) VALUES-->
<!--引入上面的的SQL-->
<include refid="commont"/>
<foreach collection="list" close=";" separator="," item="user">
(#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
小结
-
sql标签的作用:定义代码片段
id属性:唯一标识
-
include标签的作用:引用代码片段
refid属性:引用上面标识