MyBatis学习笔记①

177 阅读3分钟

简介

MyBaits的优点或者特点

1) MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
2) MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
3) MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
4) MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
  1. 普通映射:jdbc,根据反射返回对象。JavaBean字段名和数据库属性名一致,映射成对象。
  2. 高级映射:表对应实体类,一对多的关系,表与表有关系,即实体类与实体类有关系,一对象,多集合,多对一的关系。SQL语句的结果需要通过高级映射(自定义映射)处理上述关系。
  3. 手动设置参数:预编译对象拼接参数,MyBaits给出了两种方法帮助设置参数拼接SQL语句(我猜是${}和#{}那两个?
  4. XML和注解,XML常用(好像与spring实战中讲过的类似)
  5. 只需要接口不需要实体类了,“将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录”这就是MyBatis操作SQL的过程。
  6. Hibernate是全自动可以生成表和SQL语句。
  7. ORM实体类的对象与关系型数据库中的数据进行映射。

和其它持久化层技术对比

  • JDBC SQL与JAVA代码耦合度高,把代码写死了,维护修改时需要修改源代码。代码冗长,开发效率低。
  • Hibernate和JPA 操作简单开发效率高,但复杂代码需要绕过框架,内部自动生成SQL语句不容易做特殊优化。
    反射操作多,导致数据库性能下降。
    大量字段的POJO进行部分映射比较困难。
  • MyBaits 轻量级、性能好,Java专注业务,SQL专注数据。开发效率低于Hibernate,但能接受易维护。

搭建

开发环境

IDE:idea 2019.2
构建工具:maven 3.5.4
MySQL版本:MySQL 5.7
MyBatis版本:MyBatis 3.5.7

映射文件

相关概念:ORM(Object Relationship Mapping)对象关系映射。

  • 对象:Java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系

image.png

1、映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
2、MyBatis中可以面向接口操作数据,要保证两个一致:
a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致

CRUD

增加(Create)

sqlSession

sqlSessionFactoryBuilder、sqlSessionFactory、sqlSession

tips

注意各种命名一致,mybatis的版本可能导致jdbc连接的参数变化。\

优化

  • 尝试自动提交事务 JDBC就是自动提交,只有需要手动管理事务时才会关闭自动提交。
    当业务简单时我们就希望自动提交:
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
  • 日志监测sql的执行
  1. 注入依赖
  2. 配置文件

image.png

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细

想一下假如我们都需要打印degub时,那我们当然输出fatal信息啦!
所以我们想要打印的日志级别必须大于等于这个级别的信息。

最终的打印日志

image.png

更新(Update)

  1. Mapper接口中的方法
  2. 映射文件找sql语句

接口文件添加方法

image.png

在映射文件中可以添加这样的注释,帮助理清接口方法与sql语句的关系

image.png

增添测试代码

image.png

日志文件

image.png

检索(Retrieve)

这就有点特殊了,我们需要resultType或resultMap帮助我们做一个结果的映射!!
MyBatis处理结果集需要我们指定实体类对象
查询功能的标签必须设置resultType或resultMap
resultType:设置默认的映射关系|resultMap:设置自定义的映射关系
因此我们需要把数据库表中的字段名与实体类的属性名保持一致
如果字段名与属性名不一致那我们需要resultMap,假如一致就直接默认就好了!

映射文件中的具体编写如下:

image.png

执行结果如下:

image.png

tip

增删改的返回数据是固定的,但查询结果不一定,所以要思考一下返回结果是一条还是多条,返回实体类对象还是list集合呢?

核心配置文件

之前用到了environmentsmappers标签
这玩意儿还挺金贵的,要注意顺序!顺序如下:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?\

jdbc.properties

因为键名实意,注意参数名很有可能重名,所以要加前缀

类型别名

不区分大小写

<typeAliases>
    <typeAlias type="com.example.mybatis.pojo.User" alias="User" />
</typeAliases>

若不加alias这个别名参数,那么会有默认的,默认值为类名且不区分大小写

创建路径是new以"/"分割,假如用“.”只能创建一个单独的文件夹!!!

以包为单位引入映射

以包引入mapper映射文件这时候要求:

  1. mapper接口所在的包要和映射文件所在包一致
  2. mapper接口要和映射文件的名字一致

一些简化开发的tips

设置配置文件的模板

这应该属于idea教学课程的内容吧hhh,在这里可以配置模板: image.png

封装SqlSession工具类

看样子我们总算进入到utils类啦!!!

public class SqlSessionUtils {

    public static SqlSession getSqlSession() {
        SqlSession sqlSession = null;
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sqlSession;
    }
}

mybatis获取参数的两种方式

JDBC

回顾一下JDBC的实现方式:

PreparedStatement ps1 = (PreparedStatement) connetion.prepareStatement("select * from t_user where username = + '"username"'");

PreparedStatement ps2 = (PreparedStatement) connetion.prepareStatement("select * from t_user where username = ?");
ps2.setString(1,username);

ps1很容易被sql注入不推荐!!!ps2就是占位符赋值解决单引号的问题、避免sql注入!

MyBatis

${}的本质就是字符串拼接,#{}的本质就是占位符赋值

单个参数

/**
* 根据用户id查询用户信息
* @param id
* @return
*/
User getUserById(@Param("id") int id);


<!--User getUserById(@Param("id") int id);-->
<select id="getUserById" resultType="User">
    select * from t_user where id = #{id}
    select * from t_user where id = '${id}'
</select>

其实值的获取和参数名没有关系只与位置有关,但是建议键名实意,注意${}的单引号问题!!!!

多个参数

案例:密码和用户名验证登录

MyBatis自动映射

/**
 * 验证登录
 */
User checkLogin(String username, String password);
<!--User checkLogin(String username, String password);-->
<select id="checkLogin" resultType="User">
    select * from t_user where username = #{arg0} and password = #{arg1}
    select * from t_user where username = #{arg0} and password = #{param2}
</select>
@Test
public void testCheckLogin() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.checkLogin("张三","123");
    System.out.println(user);
}

不需要用一套,arg和param可混搭

手动设置

其实mapper接口有多个参数时,我们也可以手动将参数放入一个map中存储

@Test
public void testCheckLoginByMap() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("username","admin");
    map.put("password","123");
    User user = mapper.checkLoginByMap(map);
    System.out.println(user);
}
<!--User checkLoginByMap(Map<String, Object> map);-->
<select id="checkLoginByMap" resultType="User">
    select * from t_user where username = #{username} and password = #{password}
</select>
/**
 * 验证登录(map参数
 */
User checkLoginByMap(Map<String, Object> map);

能用#{}不用${}

mapper接口方法参数是实体类属性

实体类的属性并不是成员变量,而是找到相对应的get()set()方法,而没有成员变量却有get()set()方法依旧属于属性!!

/**
 * 添加用户信息
 */
int insertUser(User user);
<!--int insertUser(User user);-->
<insert id="insertUser">
    insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>
@Test
public void testInsertUser() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int result = mapper.insertUser(new User(null,"李四","123",23,"男","1231@163.com"));
    System.out.println(result);
}

使用@Param注解命名参数

/**
 * 登录验证(使用@Param)
 */
User checkLoginByParam(@Param("username") String username,@Param("password") 
//这里其实是个缩写是@Param(value = "username")
String password);
<!--checkLoginByParam(@Param("username") String username,@Param("password") String password)-->
<select id="checkLoginByParam" resultType="User">
    select * from t_user where username = #{username} and password = #{password}
</select>
@Test
public void testCheckLoginByParam() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.checkLoginByParam("张三","123");
    System.out.println(user);
}

啊写了一天,真的写吐了,找工作hhh运气好的话是不是要写一辈子。。。
注意!!!!这里的@Param有参数书写格式的:
1.按照@Param注解的值为键;2.以param1、param2...为键

image.png

'udsdfe' not found. Available parameters are [password, param1, username, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'udsdfe' not found. Available parameters are [password, param1, username, param2]

老师建议:别搞乱七八糟的了,就实体和@Param!!

@Param源码

以debug的方式执行

image.png

下一步、进入到某个方法中、强制进入、从某个方法中返回、上一个方法、跳转到光标所在的代码中

blog.csdn.net/shtxk/artic…

来个大佬的链接,懒得截图了

image.png 底层是代理模式,proxy

image.png

提供了两个方法

image.png

image.png

image.png

接口方法

一个是要执行的sql语句,一个是sql类型

image.png

弹幕大神说是命令设计模式,根据sql不同类型,执行不同流程,反正我还没学设计模式。。。 image.png

image.png

image.png 找名字

用map集合来存参数,length是参数个数,Annotation[paramIndex]获取每个参数的注解,如果有注解是param类型就通过.value()找属性值。

getNamedParams,有paramCount,names.entrySet()循环

image.png

image.png