Mybatis框架

MyBtis框架的知识点:

1、MyBatis框架的理解

通过JDBC对数据库进行操作的基本流程:加载驱动、获取数据库连接、编写sql语句和获得statementpreparedStatemnet对象,然后就是执行sql语句并对查询结果集进行封装。可知,JDBC操作数据库存在大量的代码重复并且对查询结果集的封装是个重复且繁琐的事。Mybatis就是通过配置的方式抽取sql语句,开发人员只需要重点关注sql语句的编写即可,自动完成对结果集的封装,能够提高开发效率。

MyBatis中将需要使用到的sql语句抽取到mapper.xml配置文件(可以按业务划分编写多个)中,使用<select><insert><delete><upate>标签配置sql语句进行增删改查操作。同时MyBatis作为持久层框架需要操作数据库,肯定需要配置数据库的连接和事务。除此之外,还涉及到数据库的数据类型与java数据类型之间的转换(此处的转换不是类型兼容的转换,而是从程序中输入的java数据到实际sql使用的数据以及数据库中取出的数据到实际封进java对象的数据之间的转换),如果有需要还可以使用<typeHandler>标签配置类型转换器(MyBatis中已经内置了一些基本的类型转换器,如果业务需要可以自定义类型转换器并进行配置即可)。

MyBatis中对数据库进行操作的API,有两种方式:一种是使用SqlSession对象直接指定配置文件中的某条sql语句进行增删改查操作(参数是sql的唯一标识”namespace.id”,其中namespacemapper.xml红配置的命名空间,id是每条sql的标识,因为可以存在多个mapper文件,两者可以共同组成sql语句的唯一标识),一种使用SqlSession对象针对每个mapper.xml文件生成一个动态代理对象(参数是mapper.xml文件对应的接口字节码对象),mapper.xml文件中配置的每条sql语句都对应一个方法,生成的动态代理对象直接调用对应sql的方法即可(此处的动态代理对象是基于接口的,因此需要为每个mappe.xml文件创建一个接口)。

2、Mybatis中的核心配置文件

在核心配置文件中配置MyBatis操作数据库的所有信息:

1、<environments>标签配置数据库连接和事务;

2、<typeAliases>标签配置别名

在编写mapper文件的时候(抽取sql的配置文件)需要指定参数类型(类似于 prepaStatement中的占位符,用于创建动态sql)和查询结果类型(用于查询结果 集的自动封装),此时需要使用全类名,可以给全类名定义别名,方便书写;

3、<typeHandler>标签配置自定义类型转换器

数据库类型到java类型的自定义转换,MyBatis中已经内置了,满足大部分的需要, 如果业务需要,可以进行自定义;

4、<plugins>插件配置一些插件

常用的常见的分页插件,自动实现分页功能;

5、<mappers>标签引入编写的mapper.xml配置文件

6、<propertie>标签用于引入properties配置文件

可以将数据库连接信息专门抽取到一个properties文件中,方便维护,采用 <properties标签>引入properties配置文件后,可以在该核心配置文件中使用el 达式读取;

7、<settings>标签配置一些设置信息

8、<objectFactory>对象工厂;

9、<databaseIdProvider>标签;

备注:上面的所有标签是配置在<configuration>中的子标签,而且核心配置文件是采用 dtd约束,子标签需要按照一定的顺序编写的,编写顺序为<properties><settings> <typeAliases><typeHandler><objectFactory><plugins><environments> <databaseIdProvider><mappers>,如果顺序编写可以看configuration标签的报错信息, 调整顺序即可。

3、核心配置文件细节

1) <mappers>标签的书写

该标签内部有子标签,通过子标签用于加载mapper.xml配置文件,加载方式如下 几种:

<mapper resource = “相对类加载路径”>:其中加载路径格式使用/隔开;

<mapper url = “绝对路径(磁盘)”>:格式file:///var/a/b../mapper.xml

<mapper class = “全包类名”>:当使用全注解进行配置的时候,使用一个类替代 mapper.xml文件的时候使用该方法;

<package name=”基本包名”>:表示加载该包下的所有配置文件。

2)<properties>标签的书写

<properties resource = “相对类加载路径”>,然后使用${key}读取其中的数据。

3)<typeAliases>标签定义别名

<typeAliases>

<typeAlias type=”全包类名” alias=”别名”><typeAliase>

//<typeAlias package = “包名”>表示该给包下的所有实体取别名(类名或小写)

</typeAliases>

MyBatis中已经内置对Java中的基本数据类型及其包装类和String类型定义了别名, 譬如java.lang.String类型别名为stringjava.lang.Integer类型别名为int

4)<environments>标签配置数据库环境

<environments>标签中可以配置多个<environment>子标签,每个子标签用id属性 唯一标识,每一个代表一个连接,在<environments>标签中使用default属性指定需 要连接的id

备注:下面的配置基本固定,事务管理器类型和数据源类型还有其他参数,但 是基本不使用,其中${“key”}表示配置的<properties>中读取的properties文件中的 键值对。

<environments default=”development”>

<environment id=”development”>

<transactionManager type=”JDBC”>

<dataSource type=”POOLED”>

<property name =”driver” value=${jdbc.driver}>

<property name=”url” value =”${jdbc.url}”>

<property name=”username” value=”${username}”>

<property name=”password” value=”${password}”>

</dataSource>

</environment>

</environments>

5)<typeHandlers>标签用于配置自定义类型转换器

mybatis操作数据库的时候,需要传入参数,在sql中通过使用占位符获取参数并 拼接到sql中,此处存在实际传入的参数到拼接到sql语句中的值的一个转化关系, 可以对这个转换关系进行自定义。同时,才进行查询操作的时候,mybati会对查询 的结果集进行自动分装,查询的某个字段的属性会封进对象的某个属性中,在封装 之前也存在一个转化关系,也可以自定义。

上述两个阶段存在类型转换关系,正常情况下,JDBC已经封装好了转换关系,譬 java中的String类型与数据库中的varchar类型肯定能够自由转换而不报错,但 是如果从数据库中取出的是Date日期类型的,但是需要封进的date属性是数值类 型的,或者输入的参数是日期Date日期类型的,但是需要写进数据库中的date 段是数值类型的,那么内置情况下无法完成默认转换,肯定就会报错。

当然了,为了避免这种错误的产生应该保证Java类型与JDBC类型能正常转化,但 是如果根据业务需要,确实遇到这种情况,也可以进行自定义,

自定义类型转换器的方法:

编写一个类实现org.apache.ibatis.type.TypeHandler接口;

或者继承org.apache.ibatis.type.BaseTypeHandler

从而建立一个Java类型到JDBC类型的映射关系

示例如下(将Java中的DateJDBC中的long类型的转换):

public class DateTypeHandler extends BaseTypeHandler<Date> {
    /**
     * 完成java类型到jdbc类型的转换
     * 本质上完成java类型到sql语句中类型的转换,也就是设置进preparedStatement占位符中的具体值
     * @param preparedStatement
     * @param i
     * @param date
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        long time = date.getTime();
        preparedStatement.setLong(i,time);
    }

    /**
     * 这个是完成jdbc类型到java类型的转换
     * 本质上是查询到的结果集按字段采用set方法封进某个属性
     * resultSet通过getXXX根据索引或者字段名获取值,XXX表示java中的类型
     * 在set方法执行之前进行jdbc类型到java类型的转换
     * @param resultSet
     * @param s
     * @return
     * @throws SQLException
     */
    @Override
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        long l = resultSet.getLong(s);
        Date date = new Date(l);
        return date;
    }

    @Override
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        long l = resultSet.getLong(i);
        Date date = new Date(l);
        return date;
    }

    @Override
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        long l = callableStatement.getLong(i);
        Date date = new Date(l);
        return date;
    }
}

配置这个自定义的转换器:

<typeHandlers>

<typeHandler handler = “全包类名”></typeHandler>

</typeHandlers>

备注:类型转换器的本质就识别java数据的类型,譬如当传入的参数是Date类型 的时候,使用上面定义的类型转换器将其转成long类型的数值传入 preparedStatement,在将resultSet进行自动封装的时候,检测到封装的属性为Date 类型时,在通过set方法封装之前使用转换器中的方法将long类型换成Date类型 再调用set方法封进对应的属性,所以这个BaseTypeHander只需要有一个泛型即可。

备注:在spring mvc框架中,从前端获取参数信息(都是字符串),然后需要自动 封装进控制器方法的形参时,也存在类型转换的过程,那个类型转换器是实现 Converter接口,该接口的泛型是<T,S>,使用了两个泛型参数,在进行数据转换的 时候检测前端获取的数据类型信息和需要封进的形参或者对象属性的类型,当符合 转换器指定的类型时,就可以在传入形参之前完成类型的转换。

4、MyBatis中的映射配置文件

mapper.xml文件中可以将项目中需要使用到的sql抽取出来;

一级标签:

<mapper>标签:可以有<insert><update><select><sql>子标签;

备注:这个标签必须有namespaces属性,与下面四个标签的id属性构成唯一标识

二级标签:

<insert>标签:用于包裹插入语句;

<update>标签:用于包裹更新语句;

<delete>标签:用于包裹删除语句;

<select>标签:用于包裹查询语句;

备注:这四个标签必选有id属性,同时根据需要指定parameterTyperesultType属性。

<sql>标签:用于抽取sql语句中的重复部分;

备注:这个标签必须有id属性,在<insert><update><delete><select>中使用子标签

<include>通过refid引用该抽取的sql语句。

<include>标签:引入抽取的sql语句;

备注:该标签有refid属性,指向<sql>标签;

<where>标签:用于生成动态sql,配合<if><foreach>标签的使用;

备注:当<where>标签内部 有需要拼接的内容,则拼接where关键字,否则不拼接

<if>标签:用于生成动态sql,具有判断是否拼接该标签包裹内容的功能;

备注:该标签有test属性,该属性可以写表达式,返回为true则拼接包裹内容,false 则不拼接

<foreach>标签:用于生成动态sql,遍历集合元素,依次进行sql的拼接;

备注:当参数为集合时,可以使用该标签,该标签有collection属性指定参数,有open 属性表示遍历拼接集合各个元素之前,需要拼接的内容,close属性标签遍历拼接集合 中的各个元素之后,需要拼接的内容,separator属性表示遍历集合各个元素依次进行 拼接时,各个元素之间拼接的分隔符,包裹的内容表示遍历时候拼接的内容。

5、映射文件文件细节

<mapper namespace = “命名空间”>

/*如果使用生成动态代理对象进行操作,此时的命名空间需要与编写的接口全包类名相同*/

//此处user表示定义的user,查询结果自动按属性封装成user对象

<select id = “标识” resultType=”user”>

select * from user

</select>

<select id = “标识” parameterType = “int” resultType=”user”>

//此处参数类型为int,使用#{id}表示占位符

select * from user where id =#{id}

</select>

<delete id = “标识” parameterType =”user”>

//此处参数类型是user#{user.id}表示user对象的id属性

delete from user where id = #{id}

</delete>

<update id =”标识” parameterType=”user”>

update user set name = “zhangsan” where id = #{id}

</update>

备注:上述的sql语句的编写存在一个问题,编写的sql语句与传入的参数绑定,如果 参数是个对象user

select * from user where id =#{user.id} and name = #{user,name} and age = #{user.age},这条 sql语句只能查询user对象中idnameage顺序都存在的情况,如果id属性缺失就是 id = null,这样就会导致查询失败,因此MyBatismapper.xml引入了动态sql,可以使 ifwhereforeach 标签创建动态sql

4.1 动态sql语句

示例1代码如下:

//select * from user where id = ? and user =? and age =?

<select id = “findUser” parameterType = “user” resultType = “user”>

select * from user

<where>

<if test =”id!=0”>

and id = #{id}

  </if>

<if test=”name!=null”>

  and name=#{name}

</if>

<if test=”age!=0”>

and age=#{age}

</if>

</where>

</select>

 

示例2代码:

//selct * from user where id in(?.?,?)

<select id=”findIds” parameterType=”list” resultType=”user”>

select * from user

<where>

<foreach collection =”list” open = “id in (” close = “)” item=”id”> seperator = “,”>

#{id}

</foreach>

</where>

</select>

备注:动态sql就是采用逻辑判断语句进行sql的拼接;

在实例1中,使用<where>标签sql中的where子句,如果后续后内容则会拼接where 否则不会拼接;<if>标签用于判断,是否拼接该标签内包裹的内容,test属性后写表达 式,为true则拼接,否则不拼接;在示例2中,<where>标签同1<foreach>标签可以 用于遍历集合中的元素进行依次拼接,collection 属性表示集合类型,open拼接表示在 遍历元素之前的拼接内容,close表示遍历元素之后拼接的内容,separator表示遍历元 素,元素之间的拼接字符,<foreach>标签包裹的内容表示遍历需要拼接的元素。

4.2 sql片段的抽取

代码中应该避免重复代码的编写,提高代码的可维护性。sql语句语法固定,在mapper 文件中会存在重复的情况,可以对sql语句中重复的部分进行抽取,然后再使用该重复 部分的时候直接引入即可。

mapper.xml文件中有<sql>标签对sql语句重复部分进行抽取,然后再需要使用的地 方使用<include>标签进行引用;

示例如下:

<sql  id = “id”> select * from user <sql>

<select id = “select” resultType = “user”>

<include refid = “id”></include>

</select>

6、MyBatis中的API

这些API可以独立地对数据库进行操作,当MyBatisSpring框架整合之后这些API 本使用不到。

Resource类:在org.apache.ibatis.io包中,读取核心配置文件;

static InputStream getSourceAsStream(String resource);

也可采用其他方式读取;

SqlSessionFactoryBuiler类:工厂类对象,用于获取SqlSessionFactory对象;

SqlSessionFactory build(InputStream inputStream);

SqlSessionFactory类:创建sqlSession对象,该对象是操作数据库的对象;

SqlSession openSession();

该方法默认开启事务,但事务不会自动提交,需要手动 提交;

SqlSession openSession(boolean autocommit);

该方法可以设置时候默认提交事务,当autocommitfalse表示需要手动提交事务;当设置为true表示自动提 交事务。

SqlSession类:该类的对象是实际操作数据库的对象;

<T> T selectList<String statement,Object parameter>:将查找结果集自 动封装成List集合,该statement表示mapper.xml文件中sql 句的唯一标识,上述内容已经提到唯一标识采用namespace.id 表示;mapper.xml中配置sql的时候可以指定参数类型作为占位 符,这里的parameter参数表示的是占位符;

<E> E selectOne<String statement,Object parameter>:将查到结果集自 动封装成一个对象。

int insert<String statement,Object parameter>:插入数据;

int update<String statement,Object parameter>:更新数据;

int delete<String statement,Object parameter>:删除数据;

                    void commit():提交事务;

void rollback():回滚事务;

<S> S getMapper<Class<?> class>:该方法用于获取动态代理对象,参 数为每个mapper.xml文件对象的接 口字节码对象。

7、MyBatis实现Dao

通过相关API可以知道,mybatis实现dao层可以通过获取的SqlSession对象,对 mapper.xml文件中的每条sql进行独立操作;也可以,通过创建mapper.xml配置文件的 接口,生成动态代理对象(mapper.xml问价中的每条sql语句对应接口中的一个方法), 然后操作这个代理对象即可。

第一种方式的代码(代码示例如下)

InputStream is =Resource.getSourceAsStream(“sqlConfig.xml”);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

SqlSession sqlSession = sqlSessionFactory.openSession(true);

List<User> listUser = sqlSession.selectList(“userMapper.selectAll”,user)

第二种方式的代码(代码示例如下):

InputStream is =Resource.getSourceAsStream(“sqlConfig.xml”);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

SqlSession sqlSession = sqlSessionFactory.openSession(true);

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

备注:mapper.xml接口的编写

1、接口的全包类名与mapper.xml中的namespace相同;

2、接口中的方法名与mapper.xml中每条sql语句对应的id相同;

3、接口中方法的参数与mapper.xml中每条sql语句对应的parameterType相同;

4、接口中方法的返回值类型与mapper.xml中每条sql语句对的resultType相同;

如果返回值是集合,则是集合中元素的类型与resultType相同;

8、Mybatis的注解开发

8.1 基本注解开发

相对于xml配置,注解开发更加简洁,一般情况下都是用注解进行开发。

该注解开发主要是替代mapper.xml文件,mybatis的核心配置文件还是需要编写。

从上述内容可以知道,可以通过定义一个接口生成动态代理对象实现对数据库的操作, 每个接口需要对应一个mapper.xml文件,本质上就是程序读取接口字节码文件和 mapper.xml文件然后动态生成代理对象的过程,可以将这两个文件写在一起,类接口等 文件中具有固定的组员并不能随意添加内容,可以使用注解的方式将一些信息配 置 到类或接口中,然后通过反射读取到注解中的信息,因此使用注解来替代mapper.xml 中的内容。

常用的注解:

下面的注解都是用于修饰方法的,每个方法对应mapper.xml文件中的一条sql语句

@Insert:取代<insert>标签

@Select:取代<select>标签

@Delete:取代<delete>标签

@Result:实现结果集的封装

@Results:可以与result一起使用封装多个结果集

@One:用于实现11结果封装

@Many:用于实现1对多结果封装

实现基本的CRUD操作示例代码:

public interface UserMapper{

@Insert(“insert into user values(#{id},#{name},#{age})”)

public void insert(User user);

@Update(“update user set name=#{name} and age = #{age} where id = #{id})

public void update(User user);

@Delete(“delete from user where id = #{id}”)

public void delete(int id);

@Selete(“select * from user”)

public List<User> select();

}

mybatis核心配置文件中进行配置:

<mappers>

<mapper class = “上面接口的全包类名”>

//或者<mapper package=”上面接口所在的包”>

</mappers>

8.2 One注解的使用

一对一查询

代替了<assocation>标签,是多表查询的关键,在注解中用于指定子查询返回单一对象

使用格式:

@Result(column = “”,property = “” ,one = @One(select = “”))

不使用@One注解示例:

如果存在一个用户表(user)和一个订单表(oder)的时候,查询订单及其对应的用户信息。

order表中有iddateuid三个字段;

user表中有idnameage三个字段

Order类中有int idDate dateUser user属性;

User类中有int idString nameint age属性;

查询语句应该为

select *,id oid from order left join (select *,id uid from user) on order.uid = user.id;

预期的结果是查询到的结果能够自动封装得到一个User对象,然后封进Orde对象的 User属性中,其他属性按字段名称自动封装。

public interface OrderMapper1{

@Select(select *,id oid from order left join (select * uid from user) on order.uid = user.id“)

@Results(

@Result(column = “oid”,property = “id”)

@Result(colums = “date”,property = “date”)

@Result(colums = “uid”,property = “user.id”)

@Result(colums = “name”,property = “user.name”)

@Result(colums = “age”,property = “user.age”)

)

public List<Order> findOrders();

`}

备注:实现复杂查询的自动封装,其实就是依次指定每个字段与需要封进的属性值一一 对应起来即可

使用@One注解示例:

package com.mybatis;

public interface OrderMapper2{

@Select(select * from order“)

@Results(

@Result(column = “id”,property = “id”)

@Result(colums = “date”,property = “date”)

@Reslut(

property = “user”, //指定需要封进的属性值

column = “uid”, //指定进行查询的字段

javaType = User.class, //指定使用uid查询之后封进的对象

one = @One(select = “com.mybatis.OrderMapper2.findOneUser”)

 

)

 

)

public List<Order> findOrders();

@Select(“select * from user where id = #{id}”)

public User findOneUser(int id);

`}

备注:上述的自动封装其实采用的是两次查询分别进行封装的,首先select * from orde, 将查询到的id字段封进Order对象的id属性,将查询到的date字段封进date属性,然 后采用查询到的uid再进行查询,将查询的结果封按字段封进一个user对象,然后再将 user对象封进Oder对象的user属性中。

8.3 Many注解的使用

一对多查询

代替了<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。

使用格式:

@Result(column = “”,property = “”,many = @Many(select = “”))

如果存在一个用户表(user)和一个订单表(oder)的时候,查询用户及其对应的订单。

order表中有iddate三个字段;

user表中有idnameoid三个字段

Order类中有int idDate date属性;

User类中有int idString nameList<Order> orders属性;

package cpm.mybatis;

public interface UserMapper{

@Select(“select * from user”)

@Results(

@Result(column = “id”,property = “id”),

@Result(column = “name”,property = “name”)

@Result(

column = “oid”,

property = “orders”,

javaType = Order.class,

many = @Many(select = “com.mybatis.UserMapper.findUsers”)

)

 

)

@Select(“select * from user where id=#{id}”)

public List<User> findUsers(int id);

}

9、SSM框架的整合

SSM框架整合表示将SpringMVCSpringMyBatis三个框架进行整合。

之前的博客中有提到,Spring MVC配 置文件扫描的是Controller层的Java BeanSpring 扫描的是ServiceDao层,当在Controller使用@Autowired注解注入Service层中对象 的时候并不需要使用额外的配置,是自动完成注入的,这是由于SpringSpringMVC 都是Spring体系中的,已经自动完成了整合。当使用MyBatis充当持久层框架的时候, 需要在Spring容器中生成Dao层的对象,由上述的内容可知,此处的对象应该指的是 SqlSession对象通过getMapper方法获得的代理对象,如果将该代理对象配置到Spring 容器中,就完成了SpringMyBatis的整合。

SSM框架的整合中,SpringMVCSpring已经默认整合了,无须额外的配置,Spring 需要与MyBatis进行整合,整合之后可以实现controller使用service层对象,service层 使用dao层对象。

6中可知,mybatis中核心的两个对象的是SqlSessionFactory和代理对象, SqlSeesionFactory会能够读取所有的配置信息,然后可以通过SqlSession间接获取代理 对象。Spring整合Mybatis的包是mybatis-spring,引入该包,在spring.xml文件中配置

<bean id =”sqlSessionFactory” class = “org.mybatis.spring.SqlSessionFactoryBean”>

<property name =”dataSource” ref = “dataSource”>//注入dataSource

<property name = “configuration” value = “sqlMapConfig-spring.xml”>//指定mybatis 核心配置文件

</bean>

<bean class = “org.mybatis.spring.mapper.MapperScannerConfigurer”>

<property name=”package” value = “com.demo.mapper”>//指定maper文件所在的包

</bean>

备注:原始的mybatis常用的配置有<properties><environments><typeAliases><mapper>,其中<properties><environments><mapper>分别是引入properties文件、 配置数据库、引入映射文件,都可以转移到spring的配置中,首先在spring中引入 properties文件,然后配置DataSoucebean对象,然后配置SqlSessionFactoryBean对 象,然后配置MapperScannerConfigurer对象,设置需要扫描的包即可自动生成代理对 象。

posted @ 2020-05-19 00:03  wuyiping  阅读(178)  评论(0编辑  收藏  举报