注解开发
数据准备
表结构
id | int(11) |
---|---|
username | varchar(32) |
birthday | datatime |
sex | char(1) |
address | varchar(256) |
实体类属性
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
注:
1、持久层接口和持久层接口的映射配置必须在相同的包下
例如
接口层 src.main.java.com.hncj.dao.IUserDao.java
接口层的映射配置文件 src/main/resources/com/hncj/dao/IUserDao.xml
2、持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名
3、SQL 语句的配置标签< select>,< insert>,< delete>,< update>的 id 属性必须和持久层接口的方法名相同。
1 单表CRUD
1.1 根据 ID 查询
- 应用场景
单表查询操作,基本的传入参数,获取指定行信息。 - 需求分析
通过传入用户的 userId,返回该用户的所有信息
- 在持久层接口 IUserDao.java 中添加 findById 方法
User findById(Integer userId);
- 在用户的映射文件 IUserDao.xml 中配置
<select id="findById" resultType="com.hncj.domain.User" parameterType="int">
select * from user where id = #{uid}
</select>
- 添加测试方法
//核心代码
public void testFindOne(){
User user = userDao.findById(50);
System.out.println(user);
}
1.2 保持操作
- 应用场景
单表插入操作,传入参数,插入数据库。 - 需求分析
插入一个新增用户,使其 userId 是自增长,其他字段信息是自定义的。
- 在持久层接口 IUserDao.java 中添加 findById 方法
int saveUser(User user);
- 在用户的映射文件 IUserDao.xml 中配置
<insert id="saveUser" parameterType="com.hncj.domain.User">
insert into user(username,birthday,sex,address) value(
#{username},#{birthday},#{sex},#{address}
);
</insert>
- 添加测试方法
//核心代码
public void testSave(){
User user = new User();
user.setUsername("河南人");
user.setAddress("河南");
user.setSex("男");
user.setBirthday(new Date());
userDao.saveUser(user);
//需要实现事务提交,才能插入成功
}
1.3 新增用户 id 的返回值
-
应用场景
单表插入操作,传入参数,插入数据库,并返回指定行信息。
相当于先执行一条插入操作,再执行一条查询操作并返回指定行信息。 -
需求分析
新增用户后,同时返回新增用户的 id 值。
- 在持久层接口 IUserDao.java 中添加 saveUser 方法
int saveUser(User user);
- 在用户的映射文件 IUserDao.xml 中配置
<insert id="saveUser" parameterType="com.hncj.domain.User">
<selectKey keyColumn="id" keyProperty="id“ resultType="int”
select last_insert_id();
</selectKey>
insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address}
);
</insert>
- 添加测试方法
//核心代码
public void testSave(){
Integer last_insert_id = null;
User user = new User();
user.setUsername("河南人");
user.setAddress("河南");
user.setSex("男");
user.setBirthday(new Date());
last_insert_id = userDao.saveUser(user);
System.out.println("新增用户的 ID 为:"+ last_insert_id);
//需要实现事务提交,才能插入成功
}
1.4 用户更新
- 应用场景
单表更新操作,传入参数,对指定行信息进行修改。 - 需求分析
传入 user 用户参数,将 id 字段作为修改用户信息的标志,修改其他字段。
- 在持久层接口 IUserDao.java 中添加 updateUser 方法
User updateUser(Integer userId);
- 在用户的映射文件 IUserDao.xml 中配置
<update id="updateUser" parameterType="com.hncj.domain.User">
update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
- 添加测试方法
//核心代码
public void testUpdate(){
User user = userDao.findById(50
user.setAddress("平顶山");
int res = userDao.updateUser(user);
System.out.println(res);
}
1.5 删除用户
- 应用场景
单表删除操作,传入参数,删除指定行信息。 - 需求分析
根据用户的 userId 去删除该用户信息。
- 在持久层接口 IUserDao.java 中添加 deleteUser 方法
int deleteUser(Integer userId);
- 在用户的映射文件 IUserDao.xml 中配置
<delete id="deleteUser" parameterType="int">
delete from user where id = #{uid}
</delete>
- 添加测试方法
//核心代码
public void testDelete(){
userDao.deleteUser(52);
System.out.println(res);
}
1.6 用户模糊查询
- 应用场景
单表模糊查询操作,传入参数,返回指定行信息。 - 需求分析
通过用户的 username 字段信息,去模糊查询这个用户表,并将所有符合条件的用户信息返回到一个 List 集合中。
- 在持久层接口 IUserDao.java 中添加 findByName方法
List<User> findByName(String username);
- 在用户的映射文件 IUserDao.xml 中配置
<selete id="findByName" resultType="com.hncj.domain.User" parameterType="String">
<!-- 模糊查询方式一:-->
select * from user where username like #{username}
<!-- 模糊查询方式二:-->
select * from user where username like '%${value}%'
</select>
- 添加测试方法
//核心代码
public void testFindByName(){
//模糊方式一:
List<User> users = userDao.findByName("%王%");
//模糊方式儿:
List<User> users = userDao.findByName("王");
for(User user : users)
System.out.println(user);
}
1.7 查询使用聚合函数
-
应用场景
聚合函数的使用。 -
需求分析
查询符合用户表中的用户数量。
- 在持久层接口 IUserDao.java 中添加 findTotal 方法
int findTotal();
- 在用户的映射文件 IUserDao.xml 中配置
<select id="findTotal" resultType="int">
select count(*) from user;
</select>
- 添加测试方法
//核心代码
public void testFindTotal() throws Exception {
int res = userDao.findTotal();
System.out.println(res);
}
1.8 总结
1.8.1 #{}和${}的区别
1、#{}是预编译处理,${}是字符串替换。
2、Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
3、Mybatis 在处理时 , 就 是 把 {}时,就是把时,就是把{}替换成变量的值。
4、使用#{}可以有效的防止 SQL 注入
SQL 语句的配置标签< select>,< insert>,< delete>,< update>
1.8.2 模糊查询like语句的三种书写方式
<!-- like #{}方式-->
select * from user where username like #{username}
<!-- like '%${}%'字符串拼接方式 -->
select * from user where username like '%${value}%'
<!-- like CONCAT('%', #{}, '%') 方式-->
select * from user where username like CONCAT('%', #{name}, '%')
<!-- like + bind标签 + #{}方式-->
<select id="findConcat" parameterType="String" resultType="com.hncj.domain.User" >
<bind name="name" value="'%' + _parameter + '%'"/>
select * from user where username like #{name}
</select>
1、’%${name}%’ 可能引起SQL注入,不推荐
2、"%"#{name}"%" ,这样的形式是无效的,因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果
3、可以使用 concat()函数,concat(’%’,#{name},’%’),也是一种模糊查询的方式。
<select id="findConcat" resultType="com.hncj.domain.Users" parameterType="com.test.Param">
select * from user where username like CONCAT('%', '${name}', '%')
</select>
(4)使用bind标签
1.8.2 规范化操作
1、id 属性要与持久层接口方法保持一致
2、parameterType 值为全限定类名,用于对SQL语句的占位符进行传入参数
3、resultType 值为全限定类名,用于将 SQL 语句的执行结果封装到其中
4、resultMap 在表字段名 和 类属性不一致时候,可以自定义 两者的映射关系
2 动态 SQL 语句
2.1 if标签
- 应用场景
多条件组合查询,需要在 SQL 后添加 where 1=1 用于字符拼接。 - 需求分析
根据用户信息,查询用户列表,并返回到 List 集合中。
- 在持久层接口 IUserDao.java 中添加 findByUser 方法
List<User> findByUser(User user);
- 在用户的映射文件 IUserDao.xml 中配置
<select id="findByUser" resultType="user" parameterType="user">
select * from user where 1=1
<if test="username=!=null and username!='' ">
and username like #{username}
</if>
<if test="address!=null">
and address like #{address}
</if>
</select>
<!-- <if>标签的 test 属性中写的是对象的属性名 -->
- 添加测试方法
//核心代码
public void testFindByUser() {
User u = new User();
u.setUsername("%王%");
u.setAddress("%顺义%");
List<User> users = userDao.findByUser(u);
for(User user : users)
System.out.println(user);
}
2.2 where标签
- 应用场景
多条件组合查询时,传入多个参数,简化 if 标签的 where 1=1 条件拼接。 - 需求分析
根据用户信息查询,并将查询结果返回到 List 集合中。
简化 使用 if 标签而带来的 where 1=1 的条件拼装,可以采用< where>标签来简化
<select id="findByUser" resultType="user" parameterType="user">
select * from user where 1=1
<where>
<if test="username"!=null and username!='' ">
and username like #{username}
</if>
<if test="address!=null">
and address like #{address}
</if>
</where>
</select>
2.3 foreach标签
- 应用场景
范围查询时,传入多个参数,需要将多个参数,动态添加进去。 - 需求分析
通过参入多个用户 userId 信息,将对应的用户信息返回到 List 集合中。
- 在持久层接口 IUserDao.java 中添加 findInIds 方法
List<User> findInIds(QueryVo vo);
- 在用户的映射文件 IUserDao.xml 中配置
<select id="findInIds" resultType="user" parameterType="queryvo">
select * from user where id in
<!-- include 导入简化后对的Sql 语句-->
<!--
<include refid="defaultsql"> </include>
-->
<where>
<if test="ids!=null and ids.size() > 0"
<foreach collection="ids" open="id in ("close=")" item="uid"
separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
- 添加测试方法
//核心代码
public void testFindByUser() {
QueryVo vo = new QueryVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(41);
ids.add(42);
ids.add(43);
ids.add(46);
ids.add(57);
vo.setIds(ids);
List<User> users = userDao.findInIds(vo);
for(User user : users)
System.out.println(user);
}
2.4 简化 SQL 片段
- 定义简化的 SQL 片段
<sql id="defaultSql">
select * from user
</slq>
- 引用代码片段
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>
3 多表查询
3.1 一对一
- 应用场景
跨表操作,涉及到不止一个实体类对象时。 - 需求分析
查询所有账户信息,并关联查询下单用户信息。
实体类属性
//用户实体类属性 User.java
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//账号实体类属性 Account.java
private Integer id;
private Integer uid;
private Double money;
private User user;
- 在持久层接口 AccountDao.java 中添加 findInIds 方法
List<Account> findAll();
- 在账户的映射文件 AccountDao.xml 中配置
由于表字段与类属性不能完全对应,需使用 resultMap建立映射关系
<resultMap type="account" id="accountMap"> <id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用于指定从表方的引用实体属性的 -->
<association property="user" javaType="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="findAll" resultType="accountuser">
select * a.*,u.username,u.address from account a,user u
where a.uid = u.id
</select>
- 添加测试方法
public void testFindAll() {
List<Account> accounts = accountDao.findAll();
for(Account au : accounts) {
System.out.println(au);
System.out.println(au.getUser());
}
}
3.2 一对多
- 应用场景
跨表查询,一个实体类对应多个实体类。涉及到跨表操作的 左/右外连接。 - 需求分析
用户信息和他的账户信息为一对多关系,
//账户实体类对象 Account.java
private Integer id;
private Integer uid;
private Double money;
//用户实体类对象 User.java
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
- 在持久层接口 UserDao.java 中添加 findAll 方法
List<User> findAll();
- 在用户的映射文件 UserDao.xml 中配置
由于表字段与类属性不能完全对应,需使用 resultMap建立映射关系
<select id="findAll" resultMap="userMap">
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account
a on u.id =a.uid
</select>
- 添加测试方法
public void testFindAll() {
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println("------------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
3.3 多对多
- 应用场景
跨表操作,以及中间表构建连接等操作。 - 需求分析
一个用户对应多种角色,一种角色对应多个用户。
多对多的关系映射:一个角色可以赋予多个用户
//角色实体类
private Integer roleId;
private String roleName;
private String roleDesc;
private List<User> users;
- 在持久层接口 IRoleDao.java 中添加 findAll 方法
List<Role> findAll();
- 在角色的映射文件 IRoleDao.xml 中配置
<select id="findAll" resultMap="roleMap">
select u.*,r.id as rid,r.role_name,r.role_desc from role
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
- 添加测试方法
public void testFindAll(){
List<Role> roles = roleDao.findAll();
for(Role role : roles){
System.out.println("---每个角色的信息----");
System.out.println(role);
System.out.println(role.getUsers());
}
}