Mybatis使用入门

关于Mybatis

Mybatis是一个比较流行的ORM框架,准确地说是一个半自动ORM框架,sql语句均需要自己手动编写(当然借助mybatis generator插件也可以为我们生成一些sql模版,但是框架本身是不提供的)。Mybatis被推崇的原因,一是有人说他灵活,有时候自己手写的sql语句查询效率更高(这点存疑,因为从实践看来大多数时候手写的sql语句与一些全自动ORM框架生成的效率相当,有时候甚至更低)。二是简单易用,基本只要会编写sql语句都能快速上手。

使用Mybatis

  • 环境搭建
    使用IDEA新建一个简单的Java项目,然后从maven官网上下载以下2jar包到项目目录下,并添加为Library。(jar包具体版本根据自己情况而定,其中2mysql的驱动)
  1. mybatis-3.4.6.jar
  2. mysql-connector-java-8.0.15.jar

然后再在mysql中新建一个数据库mybatisStudy,在这个数据库中新建一张Person

CREATE DATABASE mybatisStudy;
CREATE TABLE Person(
    id INT NOT NULL,
    name VARCHAR(20),
    age INT,
    PRIMARY KEY(id)
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;

再表中插入一行演示数据

INSERT INTO Person(id, name, age) VALUES(1, 'Tom', '26');
SELECT * FROM Person;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | Tom  |   26 |
+----+------+------+
1 row in set (0.00 sec)

我们知道ORM是为了将数据库某个表中的一行数据映射成一个Java对象,当我们想要对该行数据库中的某一行记录进行修改时,只要操作代表着该行记录的Java对象就可以了,简单说就是把对数据库表的操作转换为对Java对象的操作。所以我们要为Person表建立一个对应的Person类,一个Person类的实例就是Person表中的一行记录。

  • Person
public class Person {
    private int id;
    private String name;
    private int age;

    public Person(){};

    public Person(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    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 int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

之前我们说到,Mybatis是一个半自动的ORM框架,需要我们自行编写Sql语句,那我们要在哪里进行Sql语句的编写呢?答案是在xml文件中,让我们创建一个名为PersonMapper.xml的配置文件,按照mybatis的格式,把对Person表进行CRUDsql语句都写进这个xml文件里

  • PersonMapper.xml



    

后面跟着namespace定义了所有该mapper下中的sql语句的命名空间,且对每一个sql语句都还有一个该namespace下的唯一id,因此我们可以使用namespace.id的方式定位到一个sql语句。对于selectupdate, insertdelete都有相应的xml标签,我们在相应的标签下写对应的sql语句即可。这个sql语句很简单,根据idPerson表中返回一行记录,resultType定义了返回结果的类型,注意这里要写全类名,
现在我们定义好了数据库Person表对应的实体类,也在PersonMapper.xml定义了在该表上的CRUD操作。接下来我们要通过配置告诉Mybatis两件事

  1. 如何连接数据库
  2. 我们之前定义的PersonMapper.xml具体位置
  • 配置Mybatis



    
        
            
            
                
                
                
                
            
        
    
    
        
    

在这个配置文件里,我们可以定义多个environment,并设置一个默认的environment,从而可以轻易的开发环境及测试环境进行切换。在中则定义了我们之前编写的PersonMapper.xml的具体位置。现在我们已经完成了所有准备工作,接下来可以通过Mybatis来对数据库操作了。

  • Test类
public class Test {
    public static void main(String[] args) throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            Person person = session.selectOne("entity.PersonMapper.selectPerson",1);
            System.out.println(person);
        }
    }
}

我们知道要对数据库进行操作,就需要和数据库建立一条连接,Mybatis给我们提供了一个工厂类,SqlSessionFactoryBuilder,每次调用该工厂类的openSession()的方法就会返回给我们一个已经与数据库建立好的连接,接着我们在这个session内执行sql语句操作数据库了,之前我们说了namespace.id可以定位到唯一的一条sql语句, 我们调用SessionselectOne()方法,该方法可以返回一条查询记录,之前我们在定义sql语句时候制定了一个int型的参数来指定id,因此在这个方法中我们也要把这个参数传进去。

  • 实验结果
Person{id=1, name='Tom', age=26}

可以看见mybatis帮我们访问数据库,拿到了id=1的记录并包装成了一个Person类实例返回给我们。接下来我们编写除了select以外的其他3种数据库操作类型,来完善这个实验。

  • PersonMapper.xml

        






        INSERT INTO Person (id, name, age) VALUES (#{id}, #{name}, #{age})
    


        DELETE FROM Person
        WHERE id = #{id}
    


        UPDATE Person SET
        name = #{name},
        age  = #{age}
        WHERE id = #{id}
    


id=selectAllPerson的查询语句返回Person中的所有记录,这里注意返回值类型依然为Person,代表的是每条记录的类型。在使用Mybatis时,可以调用sessionselectList()方法让其返回一个由Person类型组成的列表。

List people = session.selectList("entity.PersonMapper.selectAllPerson");

id=insertPerson的操作表示插入一行记录,一行记录由一个Person类实例表示,因此parameterTypeentity.Person。注意inset,delete,update操作都没有返回值。
id=deletePersonById的操作表示根据id删除对应的一行记录。
id=updatePerson表示根据id,更行一行记录的信息,注意mybatis中,我们至多只能传入一个参数,因此要把更新的信息打包成一个Person实例传入,所以parameterType=entity.Person

  • 更新Test类
public class Test {
   public static void main(String[] args) throws Exception {
       String resource = "mybatis-config.xml";
       InputStream inputStream = Resources.getResourceAsStream(resource);
       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

       try (SqlSession session = sqlSessionFactory.openSession()) {
           Person person = session.selectOne("entity.PersonMapper.selectPerson",1);
           System.out.println(person);

           session.insert("entity.PersonMapper.insertPerson", new Person(2,"Ben",41));
           session.commit();
           List people = session.selectList("entity.PersonMapper.selectAllPerson");
           System.out.println(people.toString());

           session.update("entity.PersonMapper.updatePerson", new Person(1, "jack", 47));
           session.commit();
           person = session.selectOne("entity.PersonMapper.selectPerson",1);
           System.out.println(person);

           session.delete("entity.PersonMapper.deletePersonById", 1);
           session.commit();
           people = session.selectList("entity.PersonMapper.selectAllPerson");
           System.out.println(people.toString());

       }
   }
}

  • 实验结果
Person{id=1, name='Tom', age=26}
[Person{id=1, name='Tom', age=26}, Person{id=2, name='Ben', age=41}]
Person{id=1, name='jack', age=47}
[Person{id=2, name='Ben', age=41}]

Process finished with exit code 0

接口搭配动态代理的方式使用Mybatis

前面我们在调用session的相关方法时,会为其指定一个sql语句,这样的方式可行,但是却容易出错,因此才实际使用中我们并不会采用这样的方法,而是采用接口搭配动态代理的方式,简单的讲,对于PersonMapper.xml文件,我们会创建和其同名的PersonMapper接口。

Mybatis使用入门_第1张图片
项目结构

且在这个接口中,我们会定义和PersonMapper.xml中每个sqlid相同的方法,并且该方法的参数和返回值也和PersonMapper.xml中的一致。例如以下的sql语句对应了PersonMapper类中的同名方法

  • PersonMapper.xml

  • PersonMapper.java
public interface PersonMapper {
    Person selectPerson(int id);
}

id=selectPersonselect语句,对应了PersonMapper接口的selectPersonparameterType="int" 对应了selectPerson方法的int型参数idresultType="entity.Person"对应了该接口方法的返回值Person
基于这种方式,我们可以在接口中实现其他几条sql语句对应的接口方法。

  • 完整的PersonMapper接口
public interface PersonMapper {
    Person selectPerson(int id);
    List selectAllPerson();
    void insertPerson(Person person);
    void deletePersonById(int id);
    void updatePerson(Person person);
}

这里还有一个细节,PersonMapper类我们并不需要去实现它,Mybatis会使用动态代理的方式生成一个实现类,我们知道具体的sql语句是写在PersonMapper.xml,那么Mybatis是如何根据接口知晓我们PersonMapper.xml在哪里呢?实际上这里是约定优于配置的典型场景,回顾之前的项目结构,发现PersonMapper.java的全类名entity.PersonMapper和这前PersonMapper.xml中定义的namespace=entity.PersonMapper恰好相同,实际上Mybatis会自动的根据entity.PersonMapper这个类的全类名。去找namespace=entity.PersonMapper的映射器,在我们当前的配置中,2者恰好相同,所以没有报错,我们可以手动修改namespace来做个实验。

  • PersonMapper.xml

让我们再次运行测试类,就会报错,因为该接口找不到对应的namespace相同的映射器。

Exception in thread "main" org.apache.ibatis.binding.BindingException: Type interface entity.PersonMapper is not known to the MapperRegistry.

有了这个接口后,我们可以让Mybatis给我们生成一个对应的实现类,当我们相执行id=selectPerson这个sql语句的时候,只要调用实现类的selectPerson方法就好了。

  • Test类
public class Test {
    public static void main(String[] args) throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        try(SqlSession session = sqlSessionFactory.openSession()){
            PersonMapper personMapper = session.getMapper(PersonMapper.class);
            Person person = personMapper.selectPerson(1);
            System.out.println(person);

            List people = personMapper.selectAllPerson();
            System.out.println(people);
        }
    }
}

  • 实验结果
Person{id=1, name='TOM', age=26}
[Person{id=1, name='TOM', age=26}, Person{id=2, name='Ben', age=41}]

Process finished with exit code 0

#{ } 与 ${ }

之前在写sql语句的时候,会利用#{}这个语法来引用传递进来的参数

    

Mybatis还提供了${}语法,两者的差别整体上如下

    1. 对于基本数据类型和String, #{}中可以写任意参数名,而${}中的参数名需要指定为value,也就是说上面的sql语句,若用${}方式替换,则如下
    
    1. 对于String类型的参数,${}不会转译,而#{}会自动为其添加``。例如如下的sql语句
    

当我们调用person = personMapper.selectByName("Ben");
Mybatis为我们生成的sql语句是这样的。

SELECT * FROM Person WHERE name = Ben

这显然是错的,我们想要的是WHERE name = 'Ben'
因此我们需要手动的在${value}两侧加上单引号。

    

而如果使用#{value},则会自动就帮我们把单引号加上,那么为什么还要多此一举设计${}呢?因为我们很多时候并不想Mybatis自动帮我们转译。
比如我们设计一个sql语句,当我们传入id时,其将表中记录按照id排序,传入age时,其将表中记录按照age排序。我们就可以这样写

    
    1. 对于其他数据类型,${}#{}等价,参数名都需要和传入参数的各个属性名相同。

结果映射ResultMap

之前创建Person类的时候,Person类中的每个属性名和Person表中的字段都是一一对映的,因此当返回结果为enetity.Person时,Mybatis会自动帮我们把结果包装进对应的属性名里面,那么如果属性名和表中字段名不一致怎么办?比如我想把Person表的id字段映射到Person类的Rank字段。为了更加方便的解决这个问题,Mybatis给我们提供了ResultMap,让我们可以把自定义表中的某个列所关联的属性名。

  • 定义ResultMap
    
        
        
        
    

这里我们定义了一个ididToRankresultMaptype表示这个映射的类型,我们是要把Person表的字段映射到Person类中,因此返回的类型是entity.Person,接下来就是定义属性和表中的列的映射了,这里要注意,如果该列是主键,则要单独处理,用这个标签来定义。

  • 使用ResultMap
    

使用时只要把resultType修改为resultMap,后接对映的resultMapid即可。

利用Map作为参数和返回值

之前说过Mybatisxml中各个CURD语句的传入的参数只能有一个,当我们要更新Person表中多列时,会把各个列的值包装成一个Person类作为parameterType。除此之外,更方便的是利用Java中的Map对象作为parameterType, 列名作为key,对映的值作为value
PersonMapper.xml里添加如下语句。

    

指明了parameterType="Map", 然后Mybaits就会获取这个Map里的key=id的值替换掉占位符#{id}里的id
PersonMapper中添加对映方法,并测试它。

  • PersonMapper.java
 Person selectWithMap(Map params);

这里约定,Map的泛型中keyStringvalueObject

  • 测试
try(SqlSession session = sqlSessionFactory.openSession()){
            PersonMapper personMapper = session.getMapper(PersonMapper.class);

            Map map = new HashMap<>();
            map.put("id", 1);
            map.put("name", "TOM");
}
  • 输出结果
Person{id=1, rank=0, name='TOM', age=26}

Map除了可以作为parameterType还可以作为resultType, 当作为resultType时,Mybatis会自动把值存进与列名相同的key,但是如果在sql语句中给一列取了别名,则会以该别名为key,返回的Map同样是Map类型的。

  • PersonMapper.java
    
  • Test.java
try(SqlSession session = sqlSessionFactory.openSession()){
            PersonMapper personMapper = session.getMapper(PersonMapper.class);
            Map map = personMapper.selectPerson(1);
            for (String k : map.keySet()) {
                System.out.println(k+ " " + map.get(k));
            }
}
  • 测试结果
id1 1
name TOM
age 26

你可能感兴趣的:(Mybatis使用入门)