前言:
小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个Mybatis基础学习系列是用来记录我学习Mybatis框架基础知识的全过程 (这个系列是参照B站狂神的Mybatis最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将尽量以两天一更的速度更新这个系列,还没有学习Mybatis3框架的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。最后,希望能够和大家一同进步吧!加油吧!少年们!
特别提醒:如果对Mybatis基础学习系列感兴趣,可以阅读本系列往期博客:
第一篇:Mybatis基础学习之初识Mybatis
第二篇:Mybatis基础学习之第一个Mybatis程序
第三篇:Mybatis基础学习之CRUD增删改查
第四篇:Mybatis基础学习之万能的Map和模糊查询
第五篇: Mybatis基础学习之配置解析(上篇)
第六篇: Mybatis基础学习之配置解析(下篇)
第七篇: Mybatis基础学习之使用ResultMap解决字段名不一致
第八篇: Mybatis基础学习之日志工厂的简单使用
第九篇: Mybatis基础学习之数据分页的简单使用
今天我们来到了Mybatis基础学习的第九站:使用注解开发。废话不多说,让我们开始今天的学习内容吧。
9.使用注解开发
9.1 面向接口编程
在之前的Java基础学习中,我们都知道Java语言一门面向对象的编程语言,很多相关书籍和资深程序员也会经常强调面向对象编程的重要性。在Java基础学习中,我们也接触到了接口的概念,而在真正的开发中,很多时候也会选择面向接口编程,那么为什么要选择面向接口编程呢?
9.1.1 为什么要选择面向接口编程?
选择面向接口的根本原因是:
- 为了解耦,提高可拓展和复用性
- 分层开发中,上层不用管具体的实现,都遵守公共的标准,使得开发变得很容易,规范性更好
9.1.2 什么是面向接口编程?
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的;这种情况下,各个对象内部是如何实现相对来说就不那么重要了。
而各个对象之间的协作关系成为系统设计的关键,小到不同类之间的通信,大到各模块之间的交互,这不仅是在设计之初需要着重考虑的,也是系统设计的主要工作内容,而面向接口编程就是指按照这种思想来编程。
9.1.3 关于接口的理解
-
接口从更深层次的理解,应是定义 (规范和约束) 与实现 (名实分离) 的分离
-
接口的本身反映了系统设计人员对系统的抽象理解
-
接口应有两类:
第一类:对一个个体的抽象,它可对应成为一个抽象体(abstract class)
第二类:对一个个体某一方面的抽象,即形成一个抽象面(interface)
-
一个个体可能有多个抽象面,抽象体和抽象面是有区别的
9.1.4 三个面向区别
- 面相对象:考虑问题时,以对象为单位,考虑它的属性及方法
- 面向过程:考虑问题时,以一个具体的流程 (事务过程) 为单位,考虑它的实现
- 面向接口:接口设计与非接口设计是针对复用技术而言的,与面向对象 (或者面向过程) 不是一个问题,更多的体现是对系统整体的架构
9.2 使用注解开发
本质:反射机制实现
底层:动态代理
9.2.1 创建编写实体类和工具类
1.编写User实体类
// User实体类
public class User {
private int id; // 用户编号
private String name; // 用户名
private String password; // 密码
// User实体类的无参构造方法
public User() {
}
// User实体类的有参构造方法
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
/**
* 字段对应的get和set方法
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// 生成对应的toString方法
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
2.编写MyBatisUtils工具类
/**
* SqlSessionFactoryBuilder (建造工厂)
* --> sqlSessionFactory (生产sqlSessio)
* --> sqlSession
*/
public class MybatisUtils {
// 获取静态变量sqlSessionFactory
private static SqlSessionFactory sqlSessionFactory;
// 静态方法体
static {
try {
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 获取工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* SqlSession提供了在数据库执行SQL命令所需的所有方法
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
9.2.2 编写接口和核心配置文件
1.编写UserMapper接口
- 注解在接口的方法上实现
public interface UserMapper {
/**
* 使用Select注解: 查询所有用户信息
*/
@Select("Select * from user")
List<User> getUsers();
}
2.编写db.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
pwd=123456
3.编写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: 核心配置文件 -->
<configuration>
<!-- 引入外部配置文件: 优先使用外部配置文件 -->
<properties resource="db.properties"></properties>
<!-- 设置标准日志输出 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 通过给包起别名 -->
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
<!-- 设置默认环境为开发环境 -->
<environments default="development">
<!-- 设置一道环境为开发环境 -->
<environment id="development">
<!-- transactionManager: 表示事务管理器, 而MyBatis的默认管理器是JDBC -->
<transactionManager type="JDBC"/>
<!-- dataSource: 表示数据源,主要作用: 连接数据库(MyBatis的默认数据源类型是POOLED,也就是有池的连接) -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
<!-- 绑定接口: 使用class文件绑定注册 -->
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
</configuration>
9.2.3 编写测试类和查看测试结果
1.编写UserMapperTest测试类
public class UserMapperTest {
@Test
public void test() {
// 获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 获取mapper接口类: 底层主要使用反射,通过反射来获取包的全类名
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//调用getUsers方法
List<User> users = mapper.getUsers();
// 使用foreach循环遍历数组
for (User user : users) {
System.out.println(user);
}
// 关闭sqlSession
sqlSession.close();
}
}
2.测试结果
结果:虽然测试成功,并没有报错,但仔细观察结果后发现password的值却为null!
9.2.4 注解开发的优缺点
优点:使用注解来映射简单语句,会使代码显得更加简洁
缺点:对于稍微复杂一点的语句,Java注解就力不从心了,并且会显得更加混乱
因此,如果需要完成很复杂的事情,最好使用xml来映射语句
9.2.5 使用断点解析原理
1.设置断点和Debug测试
- 首先在获取用户信息这行设置断点,然后在test()方法旁右击选择Debug测试
2.测试结果分析
通过sqlSession获取变量后,mapper再次获取调用sqlSession,而mapper中的mapperInterface就是mapper接口,在mapperInterface下找到了com.kuang.dao.UserMapper
sqlSession:拿到数据库配置
mapperInterface:获取接口对象
methodCache:缓存
9.3 拓展知识
9.3.1 代理模式
9.3.2 MyBatis执行流程
9.4 使用注解实现增删改查
9.4.1 使用注解实现查询用户
1.修改MyBatisUtils工具类
- 可以在工具类创建的时候实现自动提交
/**
* SqlSessionFactoryBuilder (建造工厂)
* --> sqlSessionFactory (生产sqlSessio)
* --> sqlSession
*/
public class MybatisUtils {
// 获取静态变量sqlSessionFactory
private static SqlSessionFactory sqlSessionFactory;
// 静态方法体
static {
try {
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 获取工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
*/
public static SqlSession getSqlSession() {
// 设置参数为true,实现自动提交
return sqlSessionFactory.openSession(true);
}
}
2.修改UserMapper接口
2-1 正常测试
public interface UserMapper {
/**
* 使用Select注解:通过id查询指定用户信息
* 方法存在多个参数,所有的参数前面必须加上 @Param("属性名") 注解
*/
@Select("Select * from user where id = #{id}")
User getUserById(@Param("id") int id);
}
2-2 将id改为id2
- 将@Param注解中的值id修改为id2
public interface UserMapper {
/**
* 使用Select注解:通过查询指定用户信息
* 方法存在多个参数,所有的参数前面必须加上 @Param("属性名") 注解
*/
@Select("Select * from user where id = #{id}")
User getUserById(@Param("id") int id2);
}
- 将方法参数中的id修改为id2
public interface UserMapper {
/**
* 使用Select注解:通过查询指定用户信息
* 方法存在多个参数,所有的参数前面必须加上 @Param("属性名") 注解
*/
@Select("Select * from user where id = #{id}")
User getUserById(@Param("id2") int id);
}
3.编写UserMapperTest测试类
public class UserMapperTest {
@Test
public void getUserById() {
// 获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 获取mapper接口类(底层主要使用反射,通过反射来获取包的全类名)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 调用getUserById方法获取用户信息
User userById = mapper.getUserById(1);
// 打印查询到的用户信息
System.out.println(userById);
// 关闭sqlSession
sqlSession.close();
}
}
4.测试结果
4-1 正常测试结果
4-2 将id改为id2
- 将@Param注解中的值id修改为id2
测试结果报错:参数id找不到!
- 将方法参数中的id修改为id2
结果:测试成功!
因此,@Select注解中的id与@Param注解中的id要一致!
9.4.2 使用注解实现增加用户
1.修改UserMapper接口
public interface UserMapper {
/**
* 使用Insert注解:增加用户信息
*/
@Insert("Insert into user(id,name,pwd) values(#{id},#{name},#{password})")
int addUser(User user);
}
3.编写UserMapperTest测试类
public class UserMapperTest {
@Test
public void addUser() {
// 获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 获取mapper接口类:底层主要使用反射,通过反射来获取包的全类名
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 调用addUser方法并设置返回的结果集:实现用户信息的增加
int result = mapper.addUser(new User(6,"陈奕迅","cyx123456"));
// 判断结果集的值是否大于0
if (result>0) {
System.out.println("插入成功!");
}
// 关闭sqlSession
sqlSession.close();
}
}
4.正常测试结果
- 查看控制台输出
- 查看数据库信息
结果:成功插入一条用户信息!
5.Debug测试结果
- 在int result这行设置断点,点击addUser()方法旁的Debug测试
- 可以看到autoCommit的值由默认的false变为了true
因为在Mybatis工具类中已经设置了自动提交事务,因此测试方法中就不需要再次设置了,但是一般不建议设置自动提交事务,因为这样容易出现错误!
9.4.3 使用注解实现修改用户
2.修改UserMapper接口
public interface UserMapper {
/**
* 使用Update注解:修该指定用户信息
*/
@Update("Update user set name = #{name},pwd = #{password} where id = #{id}")
int updateUser(User user);
}
3.编写UserMapperTest测试类
public class UserMapperTest {
@Test
public void updateUser() {
// 获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 获取mapper接口类(底层主要使用反射,通过反射来获取包的全类名)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 调用updateUser方法, 实现插入用户信息
mapper.updateUser(new User(6,"张惠妹","zhm123456"));
// 关闭sqlSession
sqlSession.close();
}
}
4.测试结果
- 查看控制台输出
- 查看数据库信息
结果:成功更新一条用户信息!
9.4.4 使用注解实现删除用户
2.修改UserMapper接口
public interface UserMapper {
/**
* 使用Delete注解:删除用户信息
* @Delete注解中的uid要与@Param注解中的uid一致
*/
@Delete("Delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
}
3.编写UserMapperTest测试类
public class UserMapperTest {
@Test
public void deleteUser() {
// 获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 获取mapper接口类(底层主要使用反射,通过反射来获取包的全类)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 调用updateUser方法, 实现插入用户信息
mapper.deleteUser(4);
// 关闭sqlSession
sqlSession.close();
}
}
4.测试结果
- 查看控制台输出
- 查看数据库信息
结果:成功删除id为4的用户信息!
9.5.5 关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议加上
- 在SQL中引用就是这里的@Param注解中设定的属性名!
9.5.6 #{} 和 ${} 区别
- 一般使用 #{},能够很大程度上防止SQL注入!
- ${} 方式无法防止SQL注入
- $ 一般用于传入数据库对象,比如数据库表名
- 能用 #{} 时尽量用 #{}
好了,今天的有关 使用注解开发 的学习就到此结束啦。欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连。我们下期见,拜拜啦!