框架学习-Mybatis

Mybatis简介

原始jdbc操作问题

  • 数据库连接的频繁创建和销毁带来的资源浪费
  • sql语句在代码中耦合性太大,代码不易维护
  • 需要手动的将数据库查询的数据和实体数据进行对应字段的映射

解决方案

  • 使用数据库连接池
  • 将sql抽取到xml配置文件当中
  • 使用反射、内省等底层技术,自动将实体与表进行属性和字段的自动映射

什么是Mybatis

  • Mybatis是一个基于Java的持久层框架
  • 可以隐藏 jdbc 繁杂的 api,只需要关注 sql 语句的编写,而不需要关注 加载驱动、创建连接等繁杂的过程
  • 可以将 sql 执行的结果映射为 java 对象返回,使用了 ORM(Object Relation Mapping) 思想,解决实体与数据库映射的问题

Mybatis开发步骤

  • 添加Mybatis和mysql驱动jar包
  • 创建user表
  • 编写User实体类
  • 编写映射文件UserMapper.xml(写Sql语句)
  • 编写核心配置文件SqlMapConfig.xml
  • 测试

Mybatis映射文件 xxxMapper.xml

  • 主要用来配置Sql语句
    需要在顶部加入约束
<?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">
  • 使用sqlSession执行sql时需要传入namespace+id实现
  • 插入,删除,修改操作涉及数据库数据变化,需要使用sqlSession显示的提交事务
    sqlSession.commit()

查找

namespace:查找的命名空间
resultType:指定查找到的数据封装成的实体类型

<mapper namespace="userMapper">
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from user
    </select>
</mapper>
//执行操作 参数 namespace+id
List<User> userList = sqlSession.selectList("userMapper.findAll");

插入

parameterType:指定传入参数的类型
sql语句中使用#{实体属性名}方式引用实体的属性值

<mapper namespace="userMapper">
    <insert id="save" parameterType="com.itheima.domain.User">
        insert into user values(#{id}, #{username}, #{password})
    </insert>
</mapper>
sqlSession.insert("userMapper.save", user);
sqlSession.commit();

修改

<mapper namespace="userMapper">
    <update id="update" parameterType="com.itheima.domain.User">
        update user set username = #{username}, password = #{password} where id = #{id}
    </update>
</mapper>
sqlSession.update("userMapper.update", user);
sqlSession.commit();

删除

当parameterType传递的是单个参数时,#{任意名字}引用传递的单个参数

<mapper namespace="userMapper">
    <delete id="delete" parameterType="java.lang.Integer">
        delete from user where id = #{id}
    </delete>
</mapper>
sqlSession.delete("userMapper.delete", 3);
sqlSession.commit();

Mybatis核心配置文件 SqlMapConfig.xml

environment标签

default:指定默认使用的 数据库 环境
id:当前环境的名称
transactionManager:type一般使用 JDBC

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/test"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </dataSource>
    </environment>
</environments>

mapper标签

作用:是加载映射的标签
相对于类路径的资源引用: <mapper resource="com.itheima.mapper\UserMapper.xml"/>

properties标签

作用:加载额外的配置文件,如 jdbc.properties
<properties resource="jdbc.properties"/>

typeAliases标签

作用: 为 Java 类型设置一个别名

Mybatis已经定义好的别名
在这里插入图片描述
自定义别名

SqlMapConfig.xml

<typeAliases>
    <typeAlias type="com.itheima.domain.User" alias="user"/>
</typeAliases>

UserMapper.xml

<select id="findAll" resultType="user">
    select * from user
</select>

typeHandlers标签

  • 作用:重写类型处理器或自定义类型处理器来处理 java 数据类型和 mysql 数据库的数据类型的转换
  • 开发步骤:
    • 定义处理器类extends BaseTypeHandler<T>
    • 覆盖四个未实现的方法
      setNonNullParameter是java类型转换为数据库类型的方法
      getNullableResult是 mysql类型转换为 java的Type类型的方法
    • sqlMapConfig.xml中进行typeHandler的注册
  • 例如:将一个 java 中的 Date 类型转换为 毫秒值存入mysql 的bigint,再将 mysql 中取出的 bigint 数据转换为 Date 类型

DateTypeHandler.java

public class DateTypeHandler extends BaseTypeHandler<Date> {
    //java类型转换为数据库需要的类型
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        long time = date.getTime();
        preparedStatement.setLong(i, time);
    }

    //数据库中类型转换为java类型
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        long aLong = resultSet.getLong(s);
        return new Date(aLong);
    }

    //数据库中类型转换为java类型
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        long aLong = resultSet.getLong(i);
        return new Date(aLong);
    }

    //数据库中类型转换为java类型
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        long aLong = callableStatement.getLong(i);
        return new Date(aLong);
    }
}

sqlMapConfig.xml

<!--定义类型转换处理器-->
<typeHandlers>
    <typeHandler handler="com.itheima.handler.DateTypeHandler"/>
</typeHandlers>

plugins标签

  • 作用:导入外部插件
  • 例如:pageHelper插件简化分页操作
  • 使用步骤:
    • 导入pageHelper坐标 和 jsqlparser坐标
    • 在mybatis的核心配置文件中配置pageHelper插件
    • 测试

配置插件

<!--配置pageHelper-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!--定义方言,不同的数据库的查询关键字不同-->
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>

测试

//配置分页参数 当前页 + 页面大小
PageHelper.startPage(2, 3);

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAll();
for (User user : userList) {
    System.out.println(user);
}

//获取分页信息
PageInfo<User> pageInfo = new PageInfo<User>(userList);

System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页:" + pageInfo.getPageNum());

Mybatis相关API

SqlSessionFactoryBuilder构建SqlSessionFactory

通过加载mybatis核心文件的输入流的形式构建一个SqlSessionFactory对象

//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

Resources工具类,从类文件下加载资源文件,在org.apache.ibatis.io包中

SqlSessionFactory对象创建SqlSession对象

openSession() 默认开启一个事务,但不会自动提交
openSession(boolean autoCommit) 参数为是否自动提交,true则会自动提交

SqlSession会话对象

执行语句的方法
在这里插入图片描述
操作事务的方法
在这里插入图片描述

Mybatis的Dao层实现

手动对Dao进行实现(不常用)

Mybatis动态代理方式对Dao进行实现:代理生成接口的实现类

在这里插入图片描述

  • 在Dao中调用:
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.findAll();
  • 注意:
    namespace 与 接口的全限定名 对应
    id 与 接口中方法名 对应
    parameterType 与 接口中方法参数类型 一致
    resultType 与 接口中返回类型 一致

Mybatis映射文件深入

动态Sql语句

  • <if> <where>用于where查询条件需要变化时
    <where>标签相当于where 1 = 1,没有条件时sql后不加where
    <if>标签相当于判断,必需test属性
<select id="findByCondition" resultType="user" parameterType="user">
    select * from user
    <where>
        <if test="id != 0">
            and id = #{id}
        </if>
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="password != null">
            and password = #{password}
        </if>
    </where>
</select>
  • <foreach>
    多用于查询多个同类条件,如 select * from user where id in (1, 2, 3)
    • foreach属性
      collection:集合类型,数组用array,List用list
      open:循环前要加的字符串
      close:循环后要加的字符串
      separator:循环间隔符
      item:负责接收集合中的每一个值,可以随意命名,类似于java的增强for
<select id="findByIds" parameterType="list" resultType="user">
    select * from user
    <where>
        <foreach collection="list" open="id in(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </where>
</select>

Sql片段的抽取

解耦合

<!--抽取的片段-->
<sql id="selectUser">select * from user</sql>

-- 引用片段
<include refid="selectUser"/>

Mybatis多表查询

一对一查询

  • 用户和订单的关系:一个用户有多个订单,一个订单只属于一个用户
    那么查询一个订单的用户为 一对一查询

  • 数据库中:orders表有主键 id 外键 uid,user表有主键 id

实体类 Order,User

public class Order {
    private int id;
    private Date orderTime;
    private double total;
    //当前订单属于哪一个用户
    private User user;

	//getter+setter...
}

public class User {
    private int id;
    private String username;
    private String password;
    private Date birthday;
}

OrderMapper.xml

<mapper namespace="com.itheima.mapper.OrderMapper">
    <!--手动指定要封装的映射类型
        column:数据库字段名
        property:实体属性名
    -->
    <resultMap id="orderMap" type="order">
        <id column="id" property="id"/>
        <result column="ordertime" property="orderTime"/>
        <result column="total" property="total"/>
        <!--
            property:order中的属性名称(private User user)
            javaType:order中的属性的类型(User)
        -->
        <association property="user" javaType="user">
            <id column="uid" property="id"/>
            <result column="username" property="username"/>
            <result column="password" property="password"/>
            <result column="birthday" property="birthday"/>
        </association>
    </resultMap>
    
    <select id="findAll" resultMap="orderMap">
        select *, o.id oid from orders o, user u where u.id = o.uid
    </select>
</mapper>

此处的 resultMap的type 和 association的javaType 用typeAliases指定了别名

一对多查询

  • 查询一个用户的所有订单

实体类 User 中包含 List<Order> orderList

public class User {

    private int id;
    private String username;
    private String password;
    private Date birthday;
	//该用户所有订单
    private List<Order> orderList;
	
	//getter+setter...
}

OrderMapper.xml

<mapper namespace="com.itheima.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="birthday" property="birthday"/>
        <!--配置集合信息
            property:指定集合名称
            ofType:指定集合中的数据类型
        -->
        <collection property="orderList" ofType="order">
            <!--封装order的数据-->
            <id column="oid" property="id"/>
            <result column="ordertime" property="orderTime"/>
            <result column="total" property="total"/>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="userMap">
        select *, o.id oid from  user u, orders o where u.id = o.uid
    </select>
</mapper>

此处的 resultMap 的 Type 和 collection 的 ofType 使用 typeAliases 指定了别名

多对多查询

  • 与一对多查询类似,只是使用了一个中间表将两个多对多的表的主键作为主键,同时设置为两个表的外键

Mybatis注解开发

常用注解

配置到XxxMapper接口文件的对应方法上即可
在这里插入图片描述

一对一查询

public interface OrderMapper {
    @Select("select * from orders")
    @Results({
            @Result(id = true, property = "id",column = "id"), //id = true表示是 id 标签
            @Result(property = "ordertime",column = "ordertime"),
            @Result(property = "total",column = "total"),
            @Result(property = "user",	   //要封装的属性名称
            		column = "uid",		   //根据哪个字段去查询user表
                    javaType = User.class, //要封装的实体类型
                    one = @One(select = "com.itheima.mapper.UserMapper.findById")) //select代表查询哪个接口的方法获得数据
    })
    List<Order> findAll();
}
public interface UserMapper {
    @Select("select * from user where id=#{id}")
    User findById(int id);
}

一对多查询

public interface UserMapper {
    @Select("select * from user")
    @Results({
            @Result(id = true, property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "password",column = "password"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "orderList",
            		column = "id",
                    javaType = List.class,
                    many = @Many(select = "com.itheima.mapper.OrderMapper.findByUid"))
    })
    List<User> findAllUserAndOrder();
}

public interface OrderMapper {
    @Select("select * from orders where uid=#{uid}")
    List<Order> findByUid(int uid);

}

多对多查询

  • 与一对多查询类似,SQL语句略有不同

SSM整合

需要添加 mybatis-spring 的 jar 包坐标

  • 思路:主要就是将SqlSessionFactory交给Spring管理 并 将事务控制使用Spring的声明式事务控制来管理,减少重复代码编写
    在这里插入图片描述
  • 将sqlSessionFactory配置到Spring容器中
<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置jdbc模板对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<!--配置MyBatis的SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:sqlMapConfig.xml"/>
</bean>
  • 扫描 Mapper,让Spring容器产生Mapper类
<!--配置Mapper扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.itheima.mapper"/>
</bean>
  • 配置声明式事务控制
<!--配置声明式事务控制-->
<bean id="transacionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transacionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
  • 修改Service实现代码
@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    public void save(Account account) {
        accountMapper.save(account);
    }
    public List<Account> findAll() {
        return accountMapper.findAll();
    }
}

总结

  • 映射文件 XxxMapper.xml 主要是进行sql语句的编写,然后对Mapper包下的接口使用动态代理的方式实现对数据库的操作,替换掉了以前的原始 dao操作。
    开发中常常使用注解直接在接口方法上进行sql语句的编写配置
  • 核心配置文件 sqlMapConfig.xml 主要是进行
    加载配置文件(properties)
    定义别名(typeAliases)
    定义数据处理转换器(typeHandlers)
    加载其他插件(plugins)
    数据库环境的指定(environments)
    加载映射文件(mappers)
  • 动态SQL语句可以使用于查询条件发生改变时的情况,常用于多条件查询;查询多个同类条件用 foreach , 查询条件会变化用 if,多个重复 SQL 片段可以抽取
  • 一对一,一对多,多对多查询的 resultType 一般用 resultMap 代替,resultMap 中显示的写出每一个关联的参数。
    常常使用注解@Results@Result代替 resultMap
  • Mybatis整合进Spring,主要就是将SqlSessionFactory交给Spring容器管理 并 将事务控制使用Spring的声明式事务控制来管理。

参考

2020年IDEA版黑马Java :BV1WZ4y1H7du

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值