MyBatis

61 阅读6分钟

MyBatis

MyBatis简介

MyBatis是一款优秀的持久层框架,属于ORM(Object Relational Mapping)映射,前身是ibatis

MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis通过开发者书写SQL语句,以及对象模型和关系模型的映射(ORM),完成对象模型和关系模型的数据转换,同时支持延迟加载、缓存、映射等。

MyBatis可以通过简单的XML或注解来配置和映射对象模型和关系模型,从而完成对象数据和关系数据的转换。

MyBatis中文网:mybatis.net.cn/

MyBatis组成

核心对象:SqlSessionFactory SqlSession

配置文件:

mybatis.cfg.xml------>主配置文件,用于配置数据源,链接各种ORM映射文件,以及实体类别名、日志等

多个ORM映射文件----->用于书写实体类和表的映射关系,操作数据库的SQL语句,以及配置持久接口

mybatis环境搭建

1、导入依赖

<dependencies>
    <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
        <version>5.1.48</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-typehandlers-jsr310</artifactId>
        <version>1.0.2</version>
    </dependency>
</dependencies>

2、建立实体类

public class ProductBean {
    /**编号*/
    private Integer id;
    /**商品名*/
    private String name;
    /**生产日期*/
    private LocalDate createTime;
    /**商品单价*/
    private Integer price;
    }

3、建立业务接口

public interface IProductService {
    public void add(ProductBean productBean);
}

4、建立mapper接口

public interface IProductMapper {
    public void add(ProductBean productBean);
}

5、在resources目录中导入主配置文件mybatis.cfg.xml

<configuration>
   <settings>
      <!--日志-->
      <setting name="logImpl" value="STDOUT_LOGGING" />
   </settings>
   <typeAliases>
      <!--给指定类,加别名-->
<!--      <typeAlias type="com.project.bean.ProductBean"></typeAlias>-->
      <!--将指定包中的所有实体类,以实体类的类名,作为别名-->
      <package name="com.project.bean"/>
   </typeAliases>
​
   <environments default="dev">  
        <environment id="dev">  
            <transactionManager type="JDBC"></transactionManager>  
            <dataSource type="POOLED">
            <!--配置驱动类-->
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <!--配置URL,allowMultiQueries=true:表示允许一个语块中,书写多条sql语句-->
            <property name="url" value="jdbc:mysql://localhost:12345/mydb?characterEncoding=utf-8&amp;allowMultiQueries=true"/>
            <!--配置用户名-->
            <property name="username" value="root"/>
            <!--配置密码-->
            <property name="password" value="root"/>
            </dataSource>  
        </environment>  
    </environments>    
   <mappers>
      <!--加载指定包中所有的mapper文件,要求mapper文件名和接口名同名,mapper文件中的命名空间,必须和接口同名-->
      <package name="com.project.mapper"/>
   </mappers>
</configuration>  

6、在resources目录中,创建包:com/project/mapper(必须和接口所在包同名),在包中导入mapper配置文件。

namespace为命名空间,必须和mapper接口同名

<mapper namespace="com.project.mapper.IProductMapper">
    <!--添加语句块,id必须和接口方法同名-->
    <insert id="add">
        insert into t_product(p_name,p_createDate,p_price)
        values(#{name},#{createTime},#{price});
    </insert>
</mapper>

7、建立service父类BaseService

/**
 * 所有业务类的父类
 */
public class BaseService {
    private static SqlSessionFactory factory;
    static {
        try {
//            从类路径中,加载主配置文件
            Reader r=Resources.getResourceAsReader("mybatis.cfg.xml");
//            建造会话工厂
            factory=new SqlSessionFactoryBuilder().build(r);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 创建并返回SqlSession对象
     * @return SqlSession对象
     */
    public SqlSession getSession(){
        return factory.openSession();
    }
}

8、建立service接口的实现类

public class ProductServiceImpl extends BaseService implements IProductService {
    @Override
    public void add(ProductBean productBean) {
        //创建并得到会话对象
        SqlSession session=this.getSession();
        //创建并返回mapper接口对象
        IProductMapper mapper=session.getMapper(IProductMapper.class);
        mapper.add(productBean);
        //提交事务
        session.commit();
        //关闭连接
        session.close();
    }
}

添加

1、在mapper文件中书写insert语句块

 <insert id="add">
        insert into t_product(p_name,p_createDate,p_price)
        values(#{name},#{createTime},#{price})
    </insert>

<!--useGeneratedKeys="true"  允许获取自动增长的值-->
<!-- keyProperty="id" 获取属性的id-->
    <insert id="addProduct" useGeneratedKeys="true" keyProperty="id">
        insert into t_product(p_name,p_createDate,p_price)
        values(#{name},#{createTime},#{price})
    </insert>

在开发中,有时候需要得到新加记录的编号,由于编号很多时候是自动增长列,由DBMS进行分配。所以,新加记录的编号,开发者是不清楚的,这时,可以在语句块添加属性useGeneratedKeys,允许得到自动增长列的值,keyProperty表示将自动增长列的值,填充实体对象的哪个属性。

<!--useGeneratedKeys="true"  允许得到自动增长列的值-->
<!-- keyProperty="id" 表示将自动增长列的值,填充实体对象的哪个属性-->
<insert id="addProduct" useGeneratedKeys="true" keyProperty="id">
    insert into t_product(p_name,p_createDate,p_price)
    values(#{name},#{createTime},#{price})
</insert>

2、书写service方法

  public void add(ProductBean productBean) {
        SqlSession session=this.getSession();
        IProductMapper mapper=session.getMapper(IProductMapper.class);
//        mapper.add(productBean);
        mapper.addProduct(productBean);
        session.commit();
        session.close();
    }

删除

1、在mapper文件中书写delete语句块

<delete id="del">
    delete from t_product where pk_productId=#{id};
</delete>

如果方法中,只有一个参数并且该参数是简单类型(int,String),占位符可以随意

2、书写service方法

public void del(Integer id) {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    mapper.del(id);
    session.commit();
    session.close();
}

修改

1、如果mapper方法中有多个参数,必须给参数加上别名(在IProductMapper接口的方法上加别名)

public void update(@Param("pid") Integer id,@Param("price") Integer price);

2、书写mapper文件,占位符和别名同名

<update id="update">
    update t_product set p_price=#{price} where pk_productId=#{pid};
</update>

3、书写service方法

public void update(Integer id, Integer price) {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    mapper.update(id,price);
    session.commit();
    session.close();
}

mybatis查询操作

在执行查询操作时,需要指定查询操作返回的结果,可以通过resultType和resultMap指定义

resultType用于指定返回的实体类型,可以时简单类型(int ,String),可以是对象类型,当返回的时实体类时,要求书写名和列名一致,否则无法根据列名封装对象的属性。

resultMap用于定义属性和数据库列的对应关系,提高重用性,其他查询语句也可以引用,当列名和属性同名时可以不用写映射。

查询所有

1、在mapper文件中,定义resultMap映射

<resultMap id="productMap" type="ProductBean">
    <id column="pk_productId" property="id"></id>
    <result column="p_name" property="name"></result>
    <result column="p_createDate" property="createTime"></result>
    <result column="p_price" property="price"></result>
</resultMap>

2、在mapper文件中,书写查询SQL

<select id="findAll" resultMap="productMap">
    select * from t_product;
</select>

3、书写业务方法

public List<ProductBean> findAll() {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    List<ProductBean> list=mapper.findAll();
    session.close();
    return list;
}

条件查询

条件查询时,mybatis有两种占位符:# 和$

#在生成sql时,对于字符类型、日期类型参数,会拼装引号

在生成sql时,不会拼装引号,可用于orderby之类的参数,使用在生成sql时,不会拼装引号,可用于order by 之类的参数,使用时容易引起sql注入

按名字模糊查询

1、在mapper文件中,书写SQL

<select id="findByName" resultMap="productMap">
    select * from t_product where p_name like "%"#{name}"%";
</select>

2、书写业务方法

public List<ProductBean> findByName(String name) {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    List<ProductBean> list=mapper.findByName(name);
    session.close();
    return list;
}

动态条件查询

1、在mapper文件中,书写SQL

<select id="findByItem" resultMap="productMap">
    select * from t_product where 1=1
    <if test="name!=null and name!=''">
        and p_name like "%"#{name}"%"
    </if>
    <if test="startDate!=null">
        and p_createDate >= #{startDate}
    </if>
    <if test="endDate!=null">
        <![CDATA[
        and p_createDate <= #{endDate}
        ]]>
    </if>
</select>

2、书写业务方法

public List<ProductBean> findByItem(String name, LocalDate startDate, LocalDate endDate) {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    List<ProductBean> list=mapper.findByItem(name, startDate, endDate);
    session.close();
    return list;
}

动态条件分页查询

1、建立业务接口

/**
 * 动态条件分页查询
 * @param pageNo 页码
 * @param name 商品名
 * @param startDate 生产起始日期
 * @param endDate 生产结束日期
 * @return 分页对象
 */
public CutPageBean<ProductBean> cutByItem(Integer pageNo, String name, LocalDate startDate, LocalDate endDate);

2、建立mapper接口方法

查询当前页的记录

/**
 * 分页查询当前页的数据
 * @param name 商品名
 * @param startDate 生产起始日期
 * @param endDate 生产结束日期
 * @param startRow 起始记录数
 * @param pageSize 查询条数
 * @return 商品集合
 */
public List<ProductBean> cutList(@Param("name") String name,
                                 @Param("startDate") LocalDate startDate,
                                 @Param("endDate") LocalDate endDate,
                                 @Param("startRow") Integer startRow,
                                 @Param("pageSize") Integer pageSize
);

统计总记录数

/**
 * 分页统计总记录数
 * @param name 商品名
 * @param startDate 生产起始日期
 * @param endDate 生产结束日期
 * @return 总记录数
 */
public Integer cutCount(@Param("name") String name,
                        @Param("startDate") LocalDate startDate,
                        @Param("endDate") LocalDate endDate);

3、在mapper文件中,书写SQL语句

如果一段SQL语句可以用于多个语句块时,可以用SQL标签独立出来,便于重用

if标签条件为真时,表示拼接指定的SQL语句。由于在XML文件中有一些特殊符号,比如:>,<,&等,这时可以用

** **包起来。这样无论里面是什么值都当字符串处理,不会因为有特殊符号,而导致编译报错。

<sql id="dynaSql">
    <if test="name!=null and name!=''">
        and p_name like "%"#{name}"%"
    </if>
    <if test="startDate!=null">
        and p_createDate >= #{startDate}
    </if>
    <if test="endDate!=null">
        <![CDATA[
        and p_createDate <= #{endDate}
        ]]>
    </if>
</sql>

具体的SQL语句,引入语句块,采用标签

<select id="cutList" resultMap="productMap">
    select * from t_product where 1=1
    <include refid="dynaSql"></include>
    limit #{startRow},#{pageSize}
</select>

<select id="cutCount" resultType="int">
    select count(*) from t_product where 1=1
    <include refid="dynaSql"></include>
</select>

4、书写业务方法

public CutPageBean<ProductBean> cutByItem(Integer pageNo, String name, LocalDate startDate, LocalDate endDate) {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    CutPageBean<ProductBean> cutPageBean=new CutPageBean<>();
    cutPageBean.setList(mapper.cutList(name,startDate,endDate,(pageNo-1)*CutPageBean.PAGESIZE,CutPageBean.PAGESIZE));
    cutPageBean.setCount(mapper.cutCount(name,startDate,endDate));
    return cutPageBean;
}

批量添加数据

1、建立业务接口

/**
 * 批量添加商品
 * @param productBeanList 商品集合
 */
public void addMore(List<ProductBean> productBeanList);

2、建立mapper接口方法

/**
 * 批量添加商品
 * @param productBeanList 商品集合
 */
public void addMore(@Param("pList") List<ProductBean> productBeanList);

3、在mapper文件中,书写SQL语句

<insert id="addMore">
    <!--collection要遍历的集合,支持list、set、数组
    item表示每次循环取出的集合元素,
    separator表示循环的分隔符-->
    insert into t_product(p_name,p_createDate,p_price) values
    <foreach collection="pList" item="product" separator=",">
        (#{product.name},#{product.createTime},#{product.price})
    </foreach>
</insert>

4、书写业务方法

public void addMore(List<ProductBean> productBeanList) {
    SqlSession session=this.getSession();
    IProductMapper mapper=session.getMapper(IProductMapper.class);
    mapper.addMore(productBeanList);
    session.commit();
    session.close();
}

myBatis注解

使用mybatis注解开发,可以在定义mapper接口时书写SQL语句。可以省去类配置文件,简洁方便。但是比较复杂的SQL和动态SQL还是建议书写类配置文件。

@Insert("insert into t_product(p_name,p_createDate,p_price)\n" +
        "        values(#{name},#{createTime},#{price})")
public void add(ProductBean productBean);
@Delete("delete from t_product where pk_productId=#{id};")
public void del(Integer id);
@Update("update t_product set p_price=#{price} where pk_productId=#{pid}")
public void update(@Param("pid") Integer id, @Param("price") Integer price);
@Select("select * from t_product")
    @ResultMap("productMap")
    public List<ProductBean> findAll();
    @Select("select * from t_product where pk_productId=#{id}")
    @ResultMap("productMap")
    public ProductBean findById(Integer id);