Mybatis简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

ORM介绍

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

Mybatis入门 - 图1

  1. 数据库的表(table --> 类(class
  2. 记录(record,行数据)--> 对象(object
  3. 字段(field)--> 对象的属性(attribute

实际应用

实际应用中即在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。
  ORM框架就是用于实现ORM技术的程序。
  常见的ORM框架有:HibernateTopLink、Castor JDO、Apache OJB等。
Java中ORM的原理: 先说ORM的实现原理,其实,要实现JavaBean的属性到数据库表的字段的映射,任何ORM框架不外乎是读某个配置文件把JavaBean的属 性和数据库表的字段自动关联起来,当从数据库Query时,自动把字段的值塞进JavaBean的对应属性里,当做INSERT或UPDATE时,自动把 JavaBean的属性值绑定到SQL语句中。
一个简单的映射例子(hibernate),我们定义User对象和数据库中user表之间的关联,user表中只有两列:id和name:

<hibernate-mapping>
  <class name="sample.orm.hibernate.User" table="user" catalog="test">
  <id name="userID" type="java.lang.Integer">
  <column name="id" />
  <generator class="assigned" />
  </id>
  <property name="userName" type="java.lang.String">
  <column name="name" />
  </property>
  </class>
  </hibernate-mapping>

即使我不了解MySQL也没有多大问题。掌握了Mybatis再看JDBC也很简单。

集成MyBatis

  • 添加Spring Web依赖
  • 添加MyBatis Framework依赖
  • 添加MySQL Driver依赖

如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

配置MySQL连接

在Spring Boot工程的application.properties文件里添加:

spring.datasource.url=jdbc:mysql://mysql数据库地址:数据库端口/数据库名称?serverTimezone=GMT%2B8
spring.datasource.username=用户名
spring.datasource.password=密码

serverTimezone=GMT%2B8这个参数设置数据库时区为中国区域。不然使用默认时区和我们当地时间相差八小时。

实战评论组件概要设计

评论产品
Mybatis入门 - 图2
添加评论
Mybatis入门 - 图3
回复评论**
Mybatis入门 - 图4

概要设计

软件工程设计角度:先对产品做模型设计,然后再看数据库的设计。
Mybatis入门 - 图5
三大功能点:

  • 用户登录
  • 写评论
  • 回复评论

抽象对象,设计领域模型,我们能够得出需要一个用户模型和评论模型。
这里一个难点就是回复评论这个动作,我们的需要设计一个Tree结构才能瞒住数据存储。当多个人评论A的时候,我们可以吧回复的评论当做是A的子,这样就形成了树结构。
Mybatis入门 - 图6

image.png

数据库设计

user、comment表

Mybatis入门 - 图8
image.png
user表
image.png
comment表
image.png

drop table if exists user;
create table user (
`id` bigint not null primary key auto_increment,
`user_name` varchar(20) not null,
`pwd` varchar(32) not null,
`nick_name` varchar(20),
`avator` varchar(20),
`gmt_created` datetime not null,
`gmt_modified` datetime not null
);
drop table if exists comment;
create table comment (
`id` bigint not null primary key auto_increment,
`ref_id` varchar(32) not null,
`user_id` bigint not null,
`contentt` varchar(1000) not null,
`parent_id` bigint,
`gmt_created` datetime not null,
`gmt_modified` datetime not null
);
insert into user (user_name,pwd,nick_name,gmt_created,gmt_modified) values(
'admin','123','管理员',now(),now()
)

MyBatis映射对象

DO对象规则

所有的ORM框架都需要一个Java对象来映射数据库的表,并且一一对应,我们把这类对象成为DO对象,名称规范:表名+DO
user表——>UserDO
comment——>CommentDO
ykd_user——>UserDO(常用)/YkdUserDO

DO对象包规则

DO对象放在xxx.xxx.dataobject包下,DO(data object)。
com.youkeda.comment——>com.youkeda.commentdataobject

DO对象数据类型

与POJO类并无不同,要注意的是属性要和数据库类型匹配。
image.png

创建DO对象

UserDO对象

package com.youkeda.comment.dataobject;

import java.time.LocalDateTime;

public class UserDO {

    private long id;

    private String userName;

    private String pwd;

    private String nickName;

    private String avatar;

    private LocalDateTime gmtCreated;

    private LocalDateTime gmtModified;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getAvatar() {
        return avatar;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }

    public LocalDateTime getGmtCreated() {
        return gmtCreated;
    }

    public void setGmtCreated(LocalDateTime gmtCreated) {
        this.gmtCreated = gmtCreated;
    }

    public LocalDateTime getGmtModified() {
        return gmtModified;
    }

    public void setGmtModified(LocalDateTime gmtModified) {
        this.gmtModified = gmtModified;
    }
}

MyBatis DAO

DAO层—数据层的服务,DAO层包含对数据库操作的接口和实现类。

DAO层

com.youkeda.comment.dao

定义DAO接口

package com.lzx.comment.dao;

import org.apache.ibatis.annotations.Mapper;

/**
 * @InterfaceName UserDAO
 * @Author 刘正星
 * @Date 2020/7/12 13:33
 **/
@Mapper
public interface UserDAO {

}

查询

package com.lzx.comment.dao;

import org.apache.ibatis.annotations.Mapper;

/**
 * @InterfaceName UserDAO
 * @Author 刘正星
 * @Date 2020/7/12 13:33
 **/
@Mapper
public interface UserDAO {
     @Select("SELECT id,user_name as userName,pwd,nick_name as nickName,avatar,gmt_created as gmtCreated,gmt_modified as gmtModified FROM user")
    List<UserDO> findAll();
}

@Select注解

利用 as 构建user_name 与 userName之间的映射。

插入

@Insert注解,插执行SQL语句插入的时候,会返回插入行数。返回值设置为int。

import UserDO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserDAO {

  @Insert("INSERT INTO user (user_name, pwd, nick_name,avatar,gmt_created,gmt_modified) VALUES(#{userName}, #{pwd}, #{nickName}, #{avatar},now(),now())")
  int insert(UserDO userDO);

  @Select("SELECT id,user_name as userName,pwd,nick_name as nickName,avatar,gmt_created as gmtCreated,gmt_modified as gmtModified FROM user")
  List<UserDO> findAll();

}

我们把 value 的值换成了 #{变量名} 这是MyBatis 获取动态值得方式。也就是执行userDO.getUserName()。

查看主键值

在@Insert注解上添加一个@Options主键。

 @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")

Options注解有三个参数

  • userGeneratedKeys:设置为ture,代表允许使用自增主键
  • keyColumn:设置主键字段名称,一般是id
  • keyProperty:设置DO模型的主键字段,一般都是id

    修改

    /**
       * 更新
       * @param commentDO
       * @return
       */
      @Update("UPDATE comment set user_id = #{userId},content = #{content},gmt_modified = now() where id =#{id}")
      int update(CommentDO commentDO);
    

    约定:同步修改gmtModified字段。这样就可以查看修改时间了

    删除

    MyBatis除了可以接受对象,还可以接受普通参数,如String ,int等。
    @Param('key') 这个用法在以后运用的还是比较多的,因为它可以实现自定义上下文参数,非常实用和方便

    @Delete("delete from user where id=#{id}")
      int delete(@Param("id") long id);
    

    注意@delete里的id要跟@param的id一样。

    简单查询

    通过用户名来查找:

    select * from user where user_name=? limit 1
    

    设计接口方法: ```java public interface UserDAO {

    @Select(“select id,user_name as userName,pwd,nick_name as nickName,avatar,gmt_created as gmtCreated,gmt_modified as gmtModified from user where user_name=#{userName} limit 1”) UserDO findByUserName(@Param(“userName”) String name);

}

<a name="R7a82"></a>
## API测试
```java
package com.lzx.comment.control;

import com.lzx.comment.dao.UserDAO;
import com.lzx.comment.dataobject.UserDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @ClassName UserController
 * @Author 刘正星
 * @Date 2020/7/12 13:35
 **/
@Controller
public class UserController {
    @Autowired
    private UserDAO userDAO;
    @GetMapping("/users")
    @ResponseBody
    private List<UserDO> getAll(){
        return userDAO.findAll();
    }
    @PostMapping("/user")
    public UserDO save(@RequestBody UserDO userDO){
        userDAO.insert(userDO);
        return userDO;
    }
    @PostMapping("/user/update")
    @ResponseBody
    public UserDO update(@RequestBody UserDO userDO){
        userDAO.update(userDO);
        return userDO;
    }
    @GetMapping("/user/del")
    @ResponseBody
    public boolean delete(@RequestParam("id") Long id) {
        return userDAO.delete(id) > 0;
    }
    @GetMapping("/user/findByUserName")
    @ResponseBody
    public UserDO findByUserName(@RequestParam("userName") String userName) {
        return userDAO.findByUserName(userName);
    }
}
package com.lzx.comment.control;

import com.lzx.comment.dao.CommentDAO;
import com.lzx.comment.dataobject.CommentDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @ClassName CommentController
 * @Author 刘正星
 * @Date 2020/7/12 13:36
 **/
@Controller
public class CommentController {
    @Autowired
    private CommentDAO commentDAO;
    @GetMapping("/comments")
    @ResponseBody
    public List<CommentDO> getAll(){
        return commentDAO.findAll();
    }

    @PostMapping("/comment")
    @ResponseBody
    public CommentDO save(@RequestBody CommentDO commentDO){
        commentDAO.insert(commentDO);
        return commentDO;
    }
    @PostMapping("/comment/update")
    @ResponseBody
    public CommentDO update(@RequestBody CommentDO commentDO){
        commentDAO.update(commentDO);
        return commentDO;
    }
    @GetMapping("/comment/del")
    @ResponseBody
    public boolean delete(@RequestParam("id")long id){
        return commentDAO.delete(id)>0;
    }

    @GetMapping("/comment/findByRefId")
    @ResponseBody
    public List<CommentDO> findByRefId(@RequestParam("refId")String refId){
        return commentDAO.findByRefId(refId);
    }


}