MyBatis
原始JDBC的操作问题
1.频繁创建和销毁数据库的连接会造成系统资源浪费从而影响系统性能
2.sql语句在代码中硬编码,如果要修改sql语句,就需要修改java代码,造成代码不易维护
3.查询操作时,需要手动将结果集中的数据封装到实体对象中
4.增删改查操作需要参数时,需要手动将实体对象的数据设置到sql语句的占位符
原始JDBC的操作问题解决方案
1.使用数据库连接池初始化连接资源
2.将sql语句抽取到配置文件中
3.使用反射,内省等底层技术,将实体与表进行属性与字段的自动映射
MyBatis是基于Java语言开发的持久层框架,其内部封装了JDBC,
使开发者只需关注sql语句本身,而不需要去处理加载驱动,创建连接,创建执行者等复杂操作
可以将实体对象和数据库进行自动映射(因为它采用了ORM思想(对象关系映射)不需要手动进行封装)
ORM介绍
-
ORM(Object Relational Mapping): 对象关系映射
指持久化数据和实体对象的映射模式,为了解决面对对象与关系型数据库存在的互不匹配的现象的技术
MyBatis入门
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//获取sqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //通过sqlSession工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行映射配置文件中的sql语句,并接收结果
List<Student> list = sqlSession.selectList("StudentMapper.selectAll");
//处理结果
for (Student student : list) {
System.out.println(student);
}
//释放资源
sqlSession.close();
is.close();
映射配置文件
映射配置文件包含了数据和对象之间的映射关系以及要执行的SQL语句.
<mapper>
: 核心根标签- namespace属性: 名称空间
<select>
: 查询功能标签<insert>
: 新增功能标签<update>
: 修改功能标签<delete>
: 删除功能标签
以上标签都含有相同的属性:
id 属性 : 唯一标识,配合名称空间使用
parameterType属性 : 指定参数映射的对象类型
resultType属性 : 指定结果映射的对象类型
- SQL获取参数 : #{属性名}
核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!-- MyBatis的DTD约束 ↓这里指定了核心根标签 -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration 是核心根标签 -->
<configuration>
<!-- 引入数据连接的配置文件-->
<properties resource="配置文件名.properties"/>
<!-- 配置log4j配置文件 -->
<settings>
<setting name="logImpl" value="log4j"/>
</settings>
<!-- 起别名 -->
<typeAliases>
<package name="com.zhaoyu.pojo"/>
</typeAliases>
<!-- environments配置数据库环境,环境可以有多个,default属性指定当前具体使用的是哪一个 -->
<environments default="mysql">
<!-- environment配置数据库环境 id属性唯一标识-->
<environment id="mysql">
<!-- transactionManager事务管理, type 属性 采用JDBC默认的的事务管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource数据源信息 type属性 连接池 -->
<dataSource type="POOLED">
<!-- property获取数据库连接的配置信息 根据${属性名} 获取配置文件中所对应的值-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- mappers引入映射配置文件 -->
<mappers>
<!-- mapper 引入指定的映射配置文件, resource属性指定映射配置文件的名称 -->
<!-- 如果有多个映射配置文件可以使用多个mapper进行引入 -->
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
数据库核心配置文键中的propeties标签
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/db1
username=root
password=root
起别名
-
<typeAliases>
: 为全类名起别名的父标签 -
<typeAlias>
: 为全类名起别名的子标签 -
属性:
- type :指定全类名
- alias :指定别名
-
<package>
: 为指定包下所有类起别名的子标签,(别名就是类名 小写)
<typeAliases>
<package name="com.zhaoyu.pojo"/>
</typeAliases>
系统预设了基本类型的别名可以直接使用
string -> java.Lang.String
Long -> java.Lang.Long
int -> java.Lang.Integer
double -> java.Lang.Double
boolean -> java.Lang.Boolean
…
log4j的使用
编写log4j配置文件
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
通过 <settings>
标签将log4j配置文件引入到核心配置文件中
接口代理方式实现Dao层
实现规则
1.映射配置文件中的名称空间必须和Dao层接口额和全类名相同
2.映射配置文件中的增删改查标签的id属性必须和Dao层接口的方法名相同
3.映射配置文件中的增删改查标签的parameterType属性必须和Dao层接口的方法的参数相同
4.映射配置文件中的增删改查标签的resultType属性必须和Dao层接口方法的返回值相同
动态SQL
-
if标签
select * from student <where> <if test="id != null"> id = #{id} </if> <if test="name != null"> and name = #{name} </if> <if test="age != null"> and age = #{age} </if> </where>
-
foreach标签
<where> <!-- collection 指容器对象 open sql语句开始部分 close 结束语句 item 参数类型 separator 分隔符号 --> <foreach collection="list" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </where>
-
sql片段抽取
<!-- 抽取标签 --> <sql id="片段唯一标识"> 抽取的sql语句</sql> <!-- 引入标签 --> <include refid="片段唯一标识"/>
分页插件
<!-- 在核心配置文件中配置分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<!-- 在代码中实现分页功能 -->
PageHelper.startPage(第几页,每页显示的条数);
-
PageInfo : 封装分页相关参数的功能类
获取到PageInfo对象 PageInfo info = new PageInfo<>(集合对象);
-
核心方法
- getTotal() : 获取总条数
- getPages() : 获取总页数
- getPageNum() : 获取当前页
- getPageSize() : 获取每页显示条数
- getPrePage() : 获取上一页
- getNextPage() : 获取下一页
- isIsFirstPage() : 获取是否是第一页
- isIsLastPage() : 获取是否是最后一页
分页可以将多条结果进行分页显示
多表操作
-
一对一 : 在任意一方建立外键,关联对方的主键
<!--配置字段和实体对象属性的映射关系--> <resultMap id="oneToOne" type="card"> <id column="cid" property="id"/> <result column="number" property="number"/> <!-- association : 配置被包含对象的映射关系 property : 被包含对象的变量名 javaType : 被包含对象的数据类型 --> <association property="p" javaType="person"> <id column="pid" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> </association> </resultMap> <select id="selectAll" resultMap="oneToOne"> select c.id cid,number,pid,name,age from card c,person p where c.pid = p.id </select>
-
<resultMap>
: 配置字段和对象属性的映射关系标签- id 属性: 唯一标识
- type 属性: 实体对象类型
-
<id>
: 配置主键映射关系标签 -
<result>
: 配置非主键映射关系标签- column 属性 : 表中字段名称
- property 属性 : 实体对象变量名称
-
<association>
: 配置被包含对象的映射关系标签- property 属性 : 被包含对象的变量名
- javaType 属性 : 被包含对象的数据类型
-
-
一对多 : 在多的一方建立外键,关联一的一方的主键
<resultMap id="oneToMany" type="classes"> <id column="cid" property="id"/> <result column="cname" property="name"/> <!-- collection : 配置被包含的集合对象映射关系 property : 被包含对象的变量名 ofType : 被包含对象的实际数据类型 --> <collection property="students" ofType="student"> <id column="sid" property="id"/> <result column="sname" property="name"/> <result column="sage" property="age"/> </collection> </resultMap> <select id="selectAll" resultMap="oneToMany"> select c.id cid,c.name cname,s.id sid, s.name sname, s.age sage from classes c,student s where c.id = s.cid </select>
<resultMap>
: 配置字段和对象属性的映射关系标签- id 属性 : 唯一标识
- type 属性 : 实体对象类型
<id>
: 配置主键映射关系标签<result>
: 配置非主键映射关系标签- column 属性 : 表中字段名称
- property 属性 : 实体对象变量名称
<collection>
: 配置被包含集合对象的映射关系标签- property 属性 : 被包含集合对象的变量名
- ofType 属性 : 集合中保存的对象数据类型
-
多对多 : 借助中间表,中间表至少两个字段,分别关联两张表的主键
<!-- 类似于一对多 --> <resultMap id="manyToMany" type="student"> <id column="sid" property="id"/> <result column="sname" property="name"/> <result column="sage" property="age"/> <collection property="courses" ofType="course"> <id column="cid" property="id"/> <result column="cname" property="name"/> </collection> </resultMap> <select id="selectAll" resultMap="manyToMany"> select s.id sid, s.name sname, s.age sage, c.id cid, c.name cname from student s,course c,stu_cr sc where sc.sid = s.id and sc.cid = c.id </select>
<resultMap>
: 配置字段和对象属性的映射关系标签- id 属性 : 唯一标识
- type 属性 : 实体对象类型
<id>
: 配置主键映射关系标签<result>
: 配置非主键映射关系标签- column 属性 : 表中字段名称
- property 属性 : 实体对象变量名称
<collection>
: 配置被包含集合对象的映射关系标签- property 属性 : 被包含集合对象的变量名
- ofType 属性 : 集合中保存的对象数据类型
MyBatis注解开发
配置文件中存在的弊端
映射配置文件中的名称空间必须要和映射接口的全类名保持一致
映射接口中的方法名必须和映射文件中的id保持一致
-
注解开发可以简化开发步骤,省略了映射配置文件的编写.
-
配置映射关系
<!-- 在核心配置文件中配置 --> <mappers> <package name="接口所在包"/> </mappers>
注解开发只需要在映射接口中对应的方法上使用对应的注解就可以了
常见注解
@Select("查询的SQL语句") : 执行查询操作注解
@Insert("新增的SQL语句") : 执行新增操作注解
@Update("修改的SQL语句") : 执行修改操作注解
@Delete("删除的SQL语句") : 执行删除曹组注解
MyBatis注解实现多表操作
-
一对一
@Select("select * from card") @Results({ @Result(column = "id",property = "id"), @Result(column = "number",property = "number"), @Result( property = "p", //被包含对象的变量名 javaType = Person.class, //被包含对象的实际数据类型 column = "pid", //根据查询出的card表中的pid字段来查询person表 /* one , @one 一对一固定写法 select属性 : 指定调用哪个接口中的哪个方法(该接口的全路径以及方法的名字) */ one = @One(select = "com.zhaoyu.one_to_one.PersonMapper.selectById") ) }) @Select("select * from person where id=#{id}")
- @Results : 封装映射关系的父注解
- Result[] value() : 定义了Result数组
- Result : 封装了映射关系的子注解
- column 属性 : 查询出的表中字段名称
- property 属性 : 实体对象中的属性名称
- javaType 属性 : 被包含对象的数据类型
- one 属性 : 一对一查询固定属性
- @Results : 封装映射关系的父注解
-
一对多
@Select("select * from classes") @Results({ @Result(column = "id", property = "id"), @Result(column = "name", property = "name"), @Result( property = "students", //被包含对象的变量名 javaType = List.class, //被包含对象的实际数据类型 column = "id", //根据查询出的classes表的id字段来查询student表 /* many, @Many 一对查询的固定写法 select 属性 : 指定调用哪个接口中的哪个查询方法(包名加类名加方法名) */ many = @Many(select = "com.zhaoyu.one_to_many.StudentMapper.selectByCid") ) })
- @Results : 封装了映射关系的父注解
- Result[] value() : 定义一了Result数组
- @Result : 封装映射关系的子注解
- column 属性 : 查询出的表中字段名称
- property 属性 : 实体对象中的属性名称
- javaType 属性 : 被包含对象的数据类型
- many 属性 : 一对多查询固定属性
- @Many : 一对多查询的注解
- select 属性 : 指定调用某个接口中的具体方法
- @Results : 封装了映射关系的父注解
-
多对多
//@Select("select * from student") //查询到了所有的学生信息 @Select("select distinct s.id, s.name, s.age from student s, stu_cr sc where sc.sid=s.id") //仅查询到两张表有关联的部分数据 @Results({ @Result(column = "id",property = "id"), @Result(column = "name",property = "name"), @Result(column = "age",property = "age"), @Result( property = "courses", //被包含对象的变量名 javaType = List.class, //被包含对象的实际数据类型 column = "id", //根据查询出student表的id 来作为关联条件,去查询中间表和课程表 /* many, @Many 一对多查询的固定写法 select属性 : 指定调用哪个接口中的哪个查询方法(包名加类名加方法名) */ many = @Many(select ="com.zhaoyu.many_to_many.CourseMapper.selectBySid") ) })
- @Results : 封装映射关系的父注解
- Result[] value() : 定义了Result 数组
- @Result : 封装映射关系的子注解
- column 属性 : 查询出的表中字段名称
- property 属性 : 实体对象中的属性名称
- javaType 属性 : 被包含对象的数据类型
- many 属性 : 一对多, 或者多对多的查询的固定属性
- @Many : 一对多查询 或者多对多查询的注解
- select 属性 : 指定调用某个接口中的某个方法
- @Results : 封装映射关系的父注解
MyBatis构建SQL语句
MyBatis为开发者提供了org.appache.ibatis.jdbc.SQL功能类,专门用于构建SQL语句
-
@SelectProvider : 生成查询用的SQL语句注解
- type 属性 : 生成SQL语句功能类对象
- method 属性 : 指定调用方法
return new SQL(){ { SELECT("*"); FROM("表名"); } }.toString();
-
@InsertProvider : 生成新增用的SQL语句注解
- type 属性 : 生成SQL语句功能类对象
- method 属性 : 指定调用方法
return new SQL(){ { INSERT_INTO("表名"); INTO_VALUES("#{id},#{name},#{age}"); -- 字段 } }.toString();
-
@UpdateProvider : 生成修改用的SQL语句注解
- type 属性 : 生成SQL语句功能类对象
- method 属性 : 指定调用方法
return new SQL(){ { UPDATE("表名"); SET("name=#{name}","age=#{age}"); -- 修改的字段 WHERE("id=#{id}"); -- 根据某字段 } }.toString();
-
@DeleteProvider : 生成删除用的SQL语句注解
- type 属性 : 生成SQL语句功能类对象
- method 属性 : 指定调用方法
return new SQL(){ { DELETE_FROM("表名"); WHERE("id=#{id}"); -- 根据某字段 } }.toString();