这是mybatis系列第3篇。没看前文的倡议先去【Java冢狐】公众号中查看前文,不便了解和把握

MyBatis应用详解

上篇咱们手动开发了一个MyBatis我的项目,然而咱们仅仅是编写了代码,对于整个我的项目是如何运行以及每个代码的意义都没有认真的剖析和阐明,那么接下来咱们就开始剖析每个代码的意义以及如何编写这个代码

配置MyBatis全局配置文件

要应用Mybatis来操作数据库,那么当然就须要配置数据库相干信息,这件须要在mybatis全局配置文件中进行。即全局配置的xml文件,其对整个MyBatis进行事务的反对、数据库的配置等信息的配置。咱们个别放在main/resource文件中,如下所示

<configuration>    <!-- 环境配置,能够配置多个环境 -->    <environments default="chat01">        <!--            environment用来对某个环境进行配置            id:环境标识,惟一         -->        <environment id="chat01">            <!-- 事务管理器工厂配置 -->            <transactionManager type="org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory"/>            <!-- 数据源工厂配置,应用工厂来创立数据源 -->            <dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">                <property name="driver" value="com.mysql.jdbc.Driver"/>                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?characterEncoding=UTF-8"/>                <property name="username" value="root"/>                <property name="password" value="123456"/>            </dataSource>        </environment>    </environments></configuration>

configuration元素

这个是mybatis全局配置文件的根元素,每个配置文件只有一个

environments元素

用来配置mybatis的环境信息,用来配置多个环境的,具体的一个环境应用environment元素进行配置,environment元素有个id用来标识某个具体的环境。

environments元素有个default属性,用来指定默认应用哪个环境,如下面默认应用的是chat01。

environment元素

用来配置具体的环境信息,这个元素上面有两个子元素:transactionManager和dataSource

  • transactionManager元素

用来配置事务工厂的,有个type属性,type的值必须是org.apache.ibatis.transaction.TransactionFactory接口的实现类,用来创立事务管理器对象的,TransactionFactory接口默认有2个实现:

org.apache.ibatis.transaction.managed.ManagedTransactionFactoryorg.apache.ibatis.transaction.jdbc.JdbcTransactionFactory

个别状况下咱们应用org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory这个,mybatis和其余框架集成,比方和spring集成,事务交由spring去管制。

  • dataSource元素

这个用来配置数据源的,type属性的值必须为接口org.apache.ibatis.datasource.DataSourceFactory的实现类,DataSourceFactory也是一个工厂,用来创立数据源javax.sql.DataSource对象的,mybatis中这个接口默认有3个实现类:

org.apache.ibatis.datasource.jndi.JndiDataSourceFactoryorg.apache.ibatis.datasource.pooled.PooledDataSourceFactoryorg.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory

咱们应用第2个org.apache.ibatis.datasource.pooled.PooledDataSourceFactory,这个用来创立一个数据库连接池类型的数据源,能够实现数据库连贯共用,缩小连贯反复创立销毁的工夫。
配置数据源须要指定数据库连贯的属性信息,比方:驱动、连贯db的url、用户名、明码,这个在dataSource元素上面的property中配置,property元素的格局:

<property name="属性名称" value="值"/>

创立Mapper xml文件

在mybatis中个别咱们将一个表的所有sql操作写在一个mapper xml中,个别命名为XXXMapper.xml格局。

内容如下:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="zhonghu.mybatis.chat01.UserMapper"></mapper>

mapper xml根元素为mapper,这个元素有个namespace属性,零碎中会有很多表,每个表对应一个Mapper xml,为了避免mapper文件反复,咱们须要给每个mapper xml文件须要指定一个namespace,通过这个能够辨别每个mapper xml文件,下面咱们指定为zhonghu.mybatis.chat01.UserMapper。

一会对user表的所有操作相干的sql,咱们都会写在下面这个xml中。

mybatis全局配置文件中引入Mapper xml文件

user.xml咱们写好了,如何让mybatis晓得这个文件呢,此时咱们须要在mybatis-config.xml全局配置文件中引入UserMapper.xml,在mybatis-config.xml退出上面配置:

<mappers>        <mapper resource="mapper/user.xml"/>    </mappers>

mappers元素上面有多个mapper元素,通过mapper元素的resource属性能够引入Mapper xml文件,resource是绝对于classes的门路。

下面说的都是一些配置文件,配置文件都ok了,上面咱们就须要将mybatis跑起来了,此时须要应用到mybatis中的一些java对象了。

构建SqlSessionFactory对象

//指定mybatis全局配置文件String resource = "mybatis-config.xml";//读取全局配置文件InputStream inputStream = Resources.getResourceAsStream(resource);//构建SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactory是一个接口,是一个重量级的对象,SqlSessionFactoryBuilder通过读取全局配置文件来创立一个SqlSessionFactory,创立这个对象是比拟耗时的,次要耗时在对mybatis全局配置文件的解析下面,全局配置文件中蕴含很多内容,SqlSessionFactoryBuilder通过解析这些内容,创立了一个简单的SqlSessionFactory对象,这个对象的生命周期个别和利用的生命周期是一样的,随着利用的启动而创立,随着利用的进行而完结,所以个别是一个全局对象,个别状况下一个db对应一个SqlSessionFactory对象。

构建SqlSession对象

SqlSession相当于jdbc中的Connection对象,相当于数据库的一个连贯,能够用SqlSession来对db进行操作:如执行sql、提交事务、敞开连贯等等,须要通过SqlSessionFactory来创立SqlSession对象,SqlSessionFactory中罕用的有2个办法来创立SqlSession对象,如下:

//创立一个SqlSession,默认不会主动提交事务SqlSession openSession();//创立一个SqlSession,autoCommit:指定是否主动提交事务SqlSession openSession(boolean autoCommit);

SqlSession接口中很多办法,间接用来操作db,办法清单如下,大家眼生一下:

<T> T selectOne(String statement);<T> T selectOne(String statement, Object parameter);<E> List<E> selectList(String statement);<E> List<E> selectList(String statement, Object parameter);<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);<K, V> Map<K, V> selectMap(String statement, String mapKey);<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);<T> Cursor<T> selectCursor(String statement);<T> Cursor<T> selectCursor(String statement, Object parameter);<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);void select(String statement, Object parameter, ResultHandler handler);void select(String statement, ResultHandler handler);void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);int insert(String statement);int insert(String statement, Object parameter);int update(String statement);int update(String statement, Object parameter);int delete(String statement);int delete(String statement, Object parameter);void commit();void commit(boolean force);void rollback();void rollback(boolean force);List<BatchResult> flushStatements();void close();void clearCache();Configuration getConfiguration();<T> T getMapper(Class<T> type);Connection getConnection();

下面以select结尾的能够对db进行查问操作,insert相干的能够对db进行插入操作,update相干的能够对db进行更新操作。

引入lombok反对(非必须)

Lombok能以简略的注解模式来简化java代码,进步开发人员的开发效率。例如开发中常常须要写的javabean,都须要花工夫去增加相应的getter/setter,兴许还要去写结构器、equals等办法,而且须要保护,当属性多时会呈现大量的getter/setter办法,这些显得很简短也没有太多技术含量,一旦批改属性,就容易呈现遗记批改对应办法的失误。

Lombok能通过注解的形式,在编译时主动为属性生成结构器、getter/setter、equals、hashcode、toString办法。呈现的神奇就是在源码中没有getter和setter办法,然而在编译生成的字节码文件中有getter和setter办法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。

lombok的应用步骤

  • 先在idea中装置lombok插件
    关上idea,点击File->Settings->plugins,而后搜寻Lombok Plugin,点击装置就能够了。
  • maven中引入lombok反对
<dependency>   <groupId>org.projectlombok</groupId>   <artifactId>lombok</artifactId>   <version>1.18.10</version>   <scope>provided</scope></dependency>
  • 代码中应用lombok相干性能

引入logback(非必须)

为了不便查看mybatis运行过程中产生的日志,比方:执行的sql、sql的参数、sql的执行后果等等调试信息,咱们须要引入日志框架的反对,logback是一个很好的日志框架,此处咱们就应用这个

mybatis中集成logback步骤

  • maven中引入logback反对
<dependency>   <groupId>ch.qos.logback</groupId>   <artifactId>logback-classic</artifactId>   <version>1.2.3</version></dependency>
  • src/main/resources中创立logback.xml文件:
<?xml version="1.0" encoding="UTF-8"?><configuration>   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">       <encoder>           <pattern>%d{mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>       </encoder>   </appender>   <logger name="zhonghu" level="debug" additivity="false">       <appender-ref ref="STDOUT" />   </logger></configuration>
logback.xml具体的写法不是本文探讨的范畴,有趣味的敌人能够去钻研一下logback具体的用法。

写一个测试用例

下面说了这么多,上面咱们就写一个测试类来演示一下

内容如下:

package zhonghu.mybatis.chat01;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;@Slf4jpublic class UserMapperTest {    private SqlSessionFactory sqlSessionFactory;    @Before    public void before() throws IOException {        //指定mybatis全局配置文件        String resource = "mybatis-config.xml";        //读取全局配置文件        InputStream inputStream = Resources.getResourceAsStream(resource);        //构建SqlSessionFactory对象        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        this.sqlSessionFactory = sqlSessionFactory;    }    @Test    public void sqlSession() {        SqlSession sqlSession = this.sqlSessionFactory.openSession();        log.info("{}", sqlSession);    }}

下面代码中有个@Slf4j注解,这个是lombok提供的,能够在这个类中生成上面代码:

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserTest.class);

运行一下下面的用例:sqlSession办法,输入如下:

37:52.473 [main] INFO  z.mybatis.chat01.UserMapperTest - org.apache.ibatis.session.defaults.DefaultSqlSession@2d127a61

至此咱们就搭建了一个最小化的Mybatis我的项目,前面就须要咱们依据须要编写咱们的sql文件

应用SqlSesion执行sql操作

SqlSession常见的用法

SqlSession相当于一个连贯,能够应用这个对象对db执行增删改查操作,操作结束之后须要敞开,应用步骤:

  • 获取SqlSession对象:通过该sqlSessionFactory.openSession办法获取SqlSession对象
  • 对db进行操作:应用SqlSession对象进行db操作
  • 敞开SqlSession对象:sqlSession.close();

如下所示

//获取SqlSessionSqlSession sqlSession = this.sqlSessionFactory.openSession();try {    //执行业务操作,如:增删改查} finally {    //敞开SqlSession    sqlSession.close();}

下面咱们将SqlSession的敞开放在finally块中,确保close()肯定会执行。

新增操作

需要:传入UserModel对象,而后将这个对象的数据插入到user表中。

创立一个UserModel

UserModel类,代码如下:

package zhonghu.mybatis.chat01;import lombok.*;@Getter@Setter@NoArgsConstructor@AllArgsConstructor@Builder@ToStringpublic class UserModel {    private Long id;    private String name;    private Integer age;    private Double salary;}

这个类的字段和user表对应。

user.xml中定义插入操作

咱们说过了,对user表的所有sql操作,咱们都放在user.xml中,咱们在user.xml中退出上面配置,应用insert元素定义插入操作:

<!-- insert用来定义一个插入操作     id:操作的具体标识     parameterType:指定插入操作承受的参数类型 --><insert id="insertUser" parameterType="zhonghu.mybatis.chat01.UserModel">    <![CDATA[    INSERT INTO user (id,name,age,salary) VALUES (#{id},#{name},#{age},#{salary})     ]]></insert>
  • insert元素用来定义了一个对db的insert操作
  • id:是这个操作的一个标识,一会通过mybatis执行操作的时候会通过这个namespace和id援用到这个insert操作,
  • parameterType:用来指定这个insert操作承受的参数的类型,能够是:各种javabean、map、list、collection类型的java对象,咱们这个插入承受的是UserModel对象。
  • insert元素外部定义了具体的sql,能够看到是一个insert的sql,向user表插入数据。

须要插入的值从UserModel对象中获取,取UserModel对象的的字段,应用#{字段}这种格局能够获取到UserModel中字段的值。

调用SqlSession.insert办法执行插入操作

user插入的sql咱们曾经在UserMapper中写好,此时咱们怎么调用呢?

须要调用SqlSession.insert办法:

int insert(String statement, Object parameter)

这个办法有2个参数:

  • statement:示意那个操作,值为Mapper xml的namespace.具体操作的id,如须要调用UserMapper.xml中的insertUser操作,这个值就是:
zhonghu.mybatis.chat01.UserMapper.insertUser
  • parameter:insert操作的参数,和Mapper xml中的insert中的parameterType指定的类型统一。

返回值为插入的行数。

UserTest类中新增一个测试用例:

  @Test    public void insertUser() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(false);) {            //创立UserModel对象            UserModel userModel = UserModel.builder().id(69L).name("Java冢狐").age(30).salary(50000D).build();            //执行插入操作            int result = sqlSession.insert("zhonghu.mybatis.chat01.UserMapper.insertUser", userModel);            log.info("插入影响行数:{}", result);            //提交事务            sqlSession.commit();        }    }

运行输入如下:

05:15.831 [main] DEBUG z.m.chat01.UserMapper.insertUser - ==>  Preparing: INSERT INTO user (id,name,age,salary) VALUES (?,?,?,?) 05:15.853 [main] DEBUG z.m.chat01.UserMapper.insertUser - ==> Parameters: 69(Long), Java冢狐(String), 30(Integer), 50000.0(Double)05:15.951 [main] DEBUG z.m.chat01.UserMapper.insertUser - <==    Updates: 105:15.952 [main] INFO  z.mybatis.chat01.UserMapperTest - 插入影响行数:1
输入中打印了具体的sql语句,以及sql的参数信息,能够看到Mapper xml中的#{}被替换为了?,这个应用到了jdbc中的PreparedStatement来对参数设置值。

输入中的第二行具体列出了参数的值以及每个值的类型。

第三行输入了insert的后果为1,示意插入胜利了1行记录。

下面代码中创立SqlSession,咱们应用的是sqlSessionFactory.openSession()创立的,这个办法创立的SqlSession,内部事务是非主动提交的形式,所以须要咱们手动提交:

增、删、改操作都须要提交事务
sqlSession.commit();

如果想主动提交事务,在下面在创立SqlSession的时候改为sqlSessionFactory.openSession(true),指定事务为主动提交模式,所以最初咱们不须要手动提交事务了。

更新操作

需要:传入UserModel对象,而后通过id更新数据。

UserMapper.xml中定义Update操作

应用update定义更新操作:

<!-- update用来定义一个更新操作     id:操作的具体标识     parameterType:指定操作承受的参数类型 -->    <update id="updateUser" parameterType="zhonghu.mybatis.chat01.UserModel">    <![CDATA[    UPDATE user SET name = #{name},age = #{age},salary = #{salary} WHERE id = #{id}    ]]></update>

写法和insert操作的写法相似,指定id标识、parameterType指定操作的参数类型,元素体中是具体的sql语句。

调用SqlSession.update办法执行更新操作

须要调用SqlSession.update办法:

int update(String statement, Object parameter)

这个办法有2个参数:

  • statement:示意哪个操作,值为Mapper xml的namespace.具体操作的id,如须要调用UserMapper.xml中的updateUser操作,这个值就是:
zhonghu.mybatis.chat01.UserMapper.updateUser
  • parameter:update操作的参数,和Mapper xml中的update中的parameterType指定的类型统一。

返回值为update影响行数。

UserTest类中新增一个测试用例:

    @Test    public void updateUser() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            //创立UserModel对象            UserModel userModel = UserModel.builder().id(1L).name("Java冢狐,你好").age(18).salary(5000D).build();            //执行更新操作            int result = sqlSession.update("zhonghu.mybatis.chat01.UserMapper.updateUser", userModel);            log.info("影响行数:{}", result);        }    }

运行输入:

12:17.143 [main] DEBUG z.m.chat01.UserMapper.updateUser - ==>  Preparing: UPDATE user SET name = ?,age = ?,salary = ? WHERE id = ? 12:17.163 [main] DEBUG z.m.chat01.UserMapper.updateUser - ==> Parameters: Java冢狐,你好(String), 18(Integer), 5000.0(Double), 1(Long)12:17.258 [main] DEBUG z.m.chat01.UserMapper.updateUser - <==    Updates: 112:17.258 [main] INFO  z.mybatis.chat01.UserMapperTest - 影响行数:1

删除操作

需要:依据用户的id删除对应的用户记录

UserMapper.xml中定义Delete操作

应用update元素定义删除操作:

<!-- update用来定义一个删除操作     id:操作的具体标识     parameterType:指定操作承受的参数类型 --><update id="deleteUser" parameterType="java.lang.Long">    <![CDATA[    DELETE FROM user WHERE id = #{id}    ]]></update>

写法和update操作的写法相似,指定id标识、parameterType指定操作的参数类型,用户id为Long类型的,元素体中是具体的delete语句。

调用SqlSession.update办法执行更新操作

须要调用SqlSession.delete办法:

int delete(String statement, Object parameter)

这个办法有2个参数:

  • statement:示意哪个操作,值为Mapper xml的namespace.具体操作的id,如须要调用UserMapper.xml中的deleteUser操作,这个值就是:
com.javacode2018.chat02.UserMapper.
  • parameter:delete操作的参数,和Mapper xml中的delete中的parameterType指定的类型统一。

返回值为delete影响行数。

UserTest类中新增一个测试用例:

    @Test    public void deleteUser() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            //定义须要删除的用户id            Long userId = 1L;            //执行删除操作            int result = sqlSession.delete("zhonghu.mybatis.chat01.UserMapper.deleteUser", userId);            log.info("影响行数:{}", result);        }    }

运行输入:

14:26.711 [main] DEBUG z.m.chat01.UserMapper.deleteUser - ==>  Preparing: DELETE FROM user WHERE id = ? 14:26.729 [main] DEBUG z.m.chat01.UserMapper.deleteUser - ==> Parameters: 1(Long)14:26.811 [main] DEBUG z.m.chat01.UserMapper.deleteUser - <==    Updates: 114:26.812 [main] INFO  z.mybatis.chat01.UserMapperTest - 影响行数:1

执行查问

select语句有恩多属性能够具体的配置每一条SQL语句

  • SQL语句返回值类型【残缺的类名或者别名】
  • 传入SQL语句的参数类型【倡议应用万能的map】
  • 命名空间中的惟一的标识符
  • 接口中的办法名与映射文件中的SQL语句ID一一对应

需要:查问所有用户信息

UserMapper.xml中定义Select操作

    <!-- select用来定义一个查问操作     id:操作的具体标识     resultType:指定查问后果保留的类型 -->    <select id="getUserList" resultType="zhonghu.mybatis.chat01.UserModel">    <![CDATA[    SELECT * FROM user    ]]></select>

写法和update操作的写法相似,指定id标识、parameterType指定操作的参数类型,resultType指定查问后果的类型,元素体中是具体的select语句。

调用SqlSession.select办法执行更新操作

UserTest增加一个用例:

  @Test    public void getUserList() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            //执行查问操作            List<UserModel> userModelList = sqlSession.selectList("zhonghu.mybatis.chat01.UserMapper.getUserList");            log.info("后果:{}", userModelList);        }    }

多插入几行,而后运行下面的用例,输入如下:

16:00.798 [main] DEBUG z.m.chat01.UserMapper.getUserList - ==>  Preparing: SELECT * FROM user 16:00.817 [main] DEBUG z.m.chat01.UserMapper.getUserList - ==> Parameters: 16:00.829 [main] DEBUG z.m.chat01.UserMapper.getUserList - <==      Total: 1616:00.829 [main] INFO  z.mybatis.chat01.UserMapperTest - 后果:[UserModel(id=2, name=批改冢狐, age=23, salary=50000.0), UserModel(id=3, name=批改冢狐, age=24, salary=6666.66), UserModel(id=4, name=Mybatis-1, age=19, salary=10000.0), UserModel(id=5, name=Java冢狐-2, age=25, salary=20000.0), UserModel(id=6, name=Mybatis-2, age=20, salary=20000.0), UserModel(id=7, name=Java冢狐-3, age=26, salary=30000.0), UserModel(id=8, name=Mybatis-3, age=21, salary=30000.0), UserModel(id=9, name=Java冢狐-4, age=27, salary=40000.0), UserModel(id=10, name=Mybatis-4, age=22, salary=40000.0), UserModel(id=11, name=Java冢狐-5, age=28, salary=50000.0), UserModel(id=12, name=Mybatis-5, age=23, salary=50000.0), UserModel(id=13, name=Java冢狐, age=1, salary=0.0), UserModel(id=14, name=冢狐, age=23, salary=50000.0), UserModel(id=59, name=Java冢狐, age=30, salary=50000.0), UserModel(id=69, name=Java冢狐, age=30, salary=50000.0), UserModel(id=89, name=Java冢狐, age=30, salary=50000.0)]

Mapper接口的应用

为什么须要Mapper接口

下面咱们解说了对一个表的增删改查操作,都是通过调用SqlSession中的办法来实现的,大家再来看一下SqlSession接口中方才用到的几个办法的定义:

int insert(String statement, Object parameter);int update(String statement, Object parameter);int delete(String statement, Object parameter);<E> List<E> selectList(String statement);

这些办法的特点咱们来看一下:

  • 调用这些办法,须要明确晓得statement的值,statement的值为namespace.具体操作的id,这些须要关上Mapper xml中去查看了才晓得,写起来不不便
  • parameter参数都是Object类型的,咱们基本不晓得这个操作具体类型是什么,须要查看Mapper xml才晓得,轻易传递个值,可能类型不匹配,然而只有在运行的时候才晓得有问题
  • selectList办法返回的是一个泛型类型的,通过这个办法咱们基本不晓得返回的后果的具体类型,也须要去查看Mapper xml才晓得

这些都是应用过程中不不便的状况,想要不便的应用,就须要用到mybatis中的Mapper接口,咱们能够定义一个interface,而后和Mapper xml关联起来,Mapper xml中的操作和Mapper接口中的办法会进行绑定,当咱们调用Mapper接口的办法的时候,会间接调用到Mapper xml中的操作,接口的残缺类名须要和Mapper xml中的namespace统一。

Mapper接口的用法(三步)

步骤1:定义Mapper接口

去看一下,user.xml中的namespace,是:

<mapper namespace="zhonghu.mybatis.chat01.UserMapper">

咱们创立的接口残缺的名称须要和下面的namespace的值一样,上面咱们创立一个接口UserMapper,如下:

UserMapper.xml中有4个操作,咱们须要在UserMapper接口中也定义4个操作,和UserMapper.xml的4个操作对应,如下:

package zhonghu.mybatis.chat01;import java.util.List;public interface UserMapper {    int insertUser(UserModel model);    int updateUser(UserModel model);    int deleteUser(Long userId);    List<UserModel> getUserList();}

UserMapper接口中定义了4个办法,办法的名称须要和UserMapper.xml具体操作的id值一样,这样调用UserMapper接口中的办法的时候,才会对应的找到UserMapper.xml中具体的操作。

比方调用UserMapper接口中的insertUser办法,mybatis查找的规定是:通过接口残缺名称.办法名称去Mapper xml中找到对应的操作。

步骤2:通过SqlSession获取Mapper接口对象

SqlSession中有个getMapper办法,能够传入接口的类型,获取具体的Mapper接口对象,如下:

/**   * Retrieves a mapper.   * @param <T> the mapper type   * @param type Mapper interface class   * @return a mapper bound to this SqlSession   */  <T> T getMapper(Class<T> type);

如获取UserMapper接口对象:

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

步骤3:调用Mapper接口的办法对db进行操作

如调用UserMapper接口的insert操作:

@Testpublic void insertUser() {    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {        UserMapper mapper = sqlSession.getMapper(UserMapper.class);        //创立UserModel对象        UserModel userModel = UserModel.builder().id(System.currentTimeMillis()).name("Java冢狐").age(30).salary(50000D).build();        //执行插入操作        int insert = mapper.insertUser(userModel);        log.info("影响行数:{}", insert);    }}

案例:应用Mapper接口来实现增删改查

创立一个测试类,代码如下:

package zhonghu.mybatis.chat01;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;@Slf4jpublic class UserMapperTest {    private SqlSessionFactory sqlSessionFactory;    @Before    public void before() throws IOException {        //指定mybatis全局配置文件        String resource = "mybatis-config.xml";        //读取全局配置文件        InputStream inputStream = Resources.getResourceAsStream(resource);        //构建SqlSessionFactory对象        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        this.sqlSessionFactory = sqlSessionFactory;    }    @Test    public void insertUser() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            UserMapper mapper = sqlSession.getMapper(UserMapper.class);            //创立UserModel对象            UserModel userModel = UserModel.builder().id(System.currentTimeMillis()).name("Java冢狐").age(30).salary(50000D).build();            //执行插入操作            int insert = mapper.insertUser(userModel);            log.info("影响行数:{}", insert);        }    }    @Test    public void updateUser() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            UserMapper mapper = sqlSession.getMapper(UserMapper.class);            //创立UserModel对象            UserModel userModel = UserModel.builder().id(1L).name("Java冢狐,你好").age(18).salary(5000D).build();            //执行更新操作            int result = mapper.updateUser(userModel);            log.info("影响行数:{}", result);        }    }    @Test    public void deleteUser() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            UserMapper mapper = sqlSession.getMapper(UserMapper.class);            //定义须要删除的用户id            Long userId = 1L;            //执行删除操作            int result = mapper.deleteUser(userId);            log.info("影响行数:{}", result);        }    }    @Test    public void getUserList() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            UserMapper mapper = sqlSession.getMapper(UserMapper.class);            //执行查问操作            List<UserModel> userModelList = mapper.getUserList();            userModelList.forEach(item -> {                log.info("{}", item);            });        }    }}

大家认真看一下下面的代码,这次咱们应用了UserMapper来间接调用UserMapper.xml中对应的操作,能够去运行一下感受一下成果。

Mapper接口应用时留神的几点

  • Mapper接口的残缺类名必须和对应的Mapper xml中的namespace的值统一
  • Mapper接口中办法的名称须要和Mapper xml中具体操作的id值统一
  • Mapper接口中办法的参数、返回值能够不和Mapper xml中的统一

Mapper接口的原理

这个应用java中的动静代理实现的,mybatis启动的时候会加载全局配置文件mybatis-config.xml,而后解析这个文件中的mapper元素指定的UserMapper.xml,会依据UserMapper.xml的namespace的值创立这个接口的一个动静代理,具体能够去看一下mybatis的源码,次要应用java中的Proxy实现的,应用java.lang.reflect.Proxy类中的newProxyInstance办法,咱们能够创立任意一个接口的一个代理对象:

public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)

咱们应用Proxy来模拟Mapper接口的实现:

package zhonghu.mybatis.chat01;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.List;@Slf4jpublic class UserMapperTest {    public static class UserMapperProxy implements InvocationHandler {        private SqlSession sqlSession;        private Class<?> mapperClass;        public UserMapperProxy(SqlSession sqlSession, Class<?> mapperClass) {            this.sqlSession = sqlSession;            this.mapperClass = mapperClass;        }        @Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            log.debug("invoke start");            String statement = mapperClass.getName() + "." + method.getName();            List<Object> result = sqlSession.selectList(statement);            log.debug("invoke end");            return result;        }    }    private SqlSessionFactory sqlSessionFactory;    @Before    public void before() throws IOException {        //指定mybatis全局配置文件        String resource = "mybatis-config.xml";        //读取全局配置文件        InputStream inputStream = Resources.getResourceAsStream(resource);        //构建SqlSessionFactory对象        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        this.sqlSessionFactory = sqlSessionFactory;    }    @Test    public void test1() {        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {            UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(UserMapperTest.class.getClassLoader(), new Class[]{UserMapper.class}, new UserMapperProxy(sqlSession, UserMapper.class));            log.info("{}", userMapper.getUserList());        }    }}

下面代码中:UserMapper是没有实现类的,能够通过Proxy.newProxyInstance给UserMapper接口创立一个代理对象,当调用UserMapper接口的办法的时候,会调用到UserMapperProxy对象的invoke办法。

运行一下test1用例,输入如下:

29:37.847 [main] DEBUG z.m.chat01.UserMapper.getUserList - ==>  Preparing: SELECT * FROM user 29:37.865 [main] DEBUG z.m.chat01.UserMapper.getUserList - ==> Parameters: 29:37.878 [main] DEBUG z.m.chat01.UserMapper.getUserList - <==      Total: 1629:37.878 [main] DEBUG z.mybatis.chat01.UserMapperTest - invoke end29:37.878 [main] INFO  z.mybatis.chat01.UserMapperTest - [UserModel(id=2, name=批改冢狐, age=23, salary=50000.0), UserModel(id=3, name=批改冢狐, age=24, salary=6666.66), UserModel(id=4, name=Mybatis-1, age=19, salary=10000.0), UserModel(id=5, name=Java冢狐-2, age=25, salary=20000.0), UserModel(id=6, name=Mybatis-2, age=20, salary=20000.0), UserModel(id=7, name=Java冢狐-3, age=26, salary=30000.0), UserModel(id=8, name=Mybatis-3, age=21, salary=30000.0), UserModel(id=9, name=Java冢狐-4, age=27, salary=40000.0), UserModel(id=10, name=Mybatis-4, age=22, salary=40000.0), UserModel(id=11, name=Java冢狐-5, age=28, salary=50000.0), UserModel(id=12, name=Mybatis-5, age=23, salary=50000.0), UserModel(id=13, name=Java冢狐, age=1, salary=0.0), UserModel(id=14, name=冢狐, age=23, salary=50000.0), UserModel(id=59, name=Java冢狐, age=30, salary=50000.0), UserModel(id=69, name=Java冢狐, age=30, salary=50000.0), UserModel(id=89, name=Java冢狐, age=30, salary=50000.0)]

留神下面输入的invoke start和invoke end,能够看到咱们调用userMapper.getUserList时候,被UserMapperProxy#invoke办法解决了。

Mybatis中创立Mapper接口代理对象应用的是上面这个类,大家能够去钻研一下:

public class MapperProxyFactory<T> {  private final Class<T> mapperInterface;  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();  public MapperProxyFactory(Class<T> mapperInterface) {    this.mapperInterface = mapperInterface;  }  public Class<T> getMapperInterface() {    return mapperInterface;  }  public Map<Method, MapperMethod> getMethodCache() {    return methodCache;  }  @SuppressWarnings("unchecked")  protected T newInstance(MapperProxy<T> mapperProxy) {    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);  }  public T newInstance(SqlSession sqlSession) {    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);    return newInstance(mapperProxy);  }}

最初

  • 如果感觉看完有播种,心愿能关注一下,顺便给我点个赞,这将会是我更新的最大能源,感激各位的反对
  • 欢送各位关注我的公众号【java冢狐】,专一于java和计算机基础知识,保障让你看完有所播种,不信你打我
  • 求一键三连:点赞、转发、在看。
  • 如果看完有不同的意见或者倡议,欢送多多评论一起交换。感激各位的反对以及厚爱。

——我是冢狐,和你一样酷爱编程。

欢送关注公众号“ Java冢狐”,获取最新消息