概念
Mybatis是一款半自动ORM框架
Mybatis和Hibernate/JPA的区别:
hibernate/JPA是全自动的ORM框架,大部分操作可以不用编写sql语句
结构比较复杂,对于处理复杂的sql操作,比较麻烦
核心思想是对对象的操作,就是对数据库的操作
建立了表和类之间的映射关系,在sql操作之前,通过映射关系自动生成sql语句
Mybatis是半自动ORM框架.所有操作都必须写sql语句
灵活性高,上手简单
核心思想是将查询结果和对象建立映射关系
在sql执行之后建立关系,sql语句和对象没有直接的关系
使用场景不同
在原型开发的时候Hibernate/JPA更快捷更方便
在项目的中后期,要考虑复杂的操作,或者性能的情况下,推荐使用mybatis
官方网站:mybatis – MyBatis 3 | Introduction
下载地址:Release mybatis-3.5.1 · mybatis/mybatis-3 · GitHub
Mybatis的环境搭建
1,pom.xml中引入mybatis核心依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--项目配置-->
<groupId>com.qf</groupId>
<artifactId>hello-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<!--依赖-->
<dependencies>
<!--MyBatis核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--MySql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</project>
2,创建mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--MyBatis配置-->
<configuration>
<!--JDBC环境配置、选中默认环境-->
<environments default="MySqlDB">
<!--MySql数据库环境配置-->
<environment id="MySqlDB">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--连接池-->
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- &转义& -->
<property name="url" value="jdbc:mysql://localhost:3306/x?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
</environments>
<!--Mapper注册-->
<mappers>
<!--注册Mapper文件的所在位置-->
<mapper resource="xxxMapper.xml"/>
</mappers>
</configuration>
mybatis的开发步骤
1,建表
2定义实体类
定义所需要CRUD的实体类
3定义DAO接口
根据所需DAO定义接口,以及方法
4编写Mappper.xml
在resources目录中创建Mapper.xml文件
5注册Mapper
将Mapper.xml注册到mybatis-config.xml中
6测试一
Mybatis的API的操作方式
细节
1,解决mapper.xml存放在resources以为路径中的读取问题
在pom.xml文件最后追加< build >标签,以便可以将xml文件复制到classes中,并在程序运行时正确读取。
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>*.xml</include><!-- 默认(新添加自定义则失效) -->
<include>**/*.xml</include><!-- 新添加 */代表1级目录 **/代表多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
2,properties配置文件
对于mybatis-config.xml的核心配置中,如果存在需要频繁改动的数据内容,可以提取到properties中。
#jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncpding=utf8
jdbc.username=root
jdbc.password=123456
修改mybatis-config.xml。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--添加properties配置文件路径(外部配置、动态替换)-->
<properties resource="jdbc.properties" />
<environments default="MySqlDB">
<environment id="MySqlDB">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--使用$ + 占位符-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserDaoMapper.xml" />
</mappers>
</configuration>
3类型别名
为实体类定义别名,提高书写效率
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties ... />
<!--定义别名二选一-->
<typeAliases>
<!--定义类的别名-->
<typeAlias type="com.qf.mybatis.part1.basic.User" alias="user" />
<!--自动扫描包,将原类名作为别名-->
<package name="com.qf.mybatis.part1.basic" />
</typeAliases>
...
</configuration>
4创建log4j配置文件
pom.xml添加log4j依赖
<!-- log4j日志依赖 https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
创建并配置log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 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
级别 | 描述 |
---|---|
ALL LEVEL | 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 |
DEBUG | 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 |
WARN | 输出警告信息;表明会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 |
OFF LEVEL | 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 |
Mybatis的CRUD操作
1 查询
标签:< select id="" resultType="" >
1.1序号参数绑定
public interface UserDao {
//使用原生参数绑定
public User selectUserByIdAndPwd(Integer id , String pwd);
}
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{arg0} AND password = #{arg1} <!--arg0 arg1 arg2 ...-->
</select>
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{param1} AND password = #{param2} <!--param1 param2 param3 ...-->
</select>
1.2注解参数绑定
import org.apache.ibatis.annotations.Param; //引入注解
public interface UserDao {
//使用MyBatis提供的@Param进行参数绑定
public User selectUserByIdAndPwd(@Param("id") Integer id , @Param("pwd") String pwd);
}
<select id="selectUserByIdAndPwd" resultType="user">
SELECT * FROM t_users
WHERE id = #{id} AND password = #{pwd} <!-- 使用注解值 @Param("pwd") -->
</select>
1.3模糊查询
public interface UserDao {
public List<User> selectUsersByKeyword(@Param("keyword") String keyword);
}
<mapper namespace="com.qf.mybatis.part1.different.UserDao">
<select id="selectUsersByKeyword" resultType="user">
SELECT * FROM t_users
WHERE name LIKE concat('%',#{keyword},'%') <!-- 拼接'%' -->
</select>
</mapper>
删除
标签:< delete id="" parameterType="" >
<delete id="deleteUser" parameterType="int">
DELETE FROM t_users
WHERE id = #{id} <!--只有一个参数时,#{任意书写}-->
</delete>
修改
标签:< update id="" parameterType="" >
<update id="updateUser" parameterType="user">
UPDATE t_users SET name=#{name}, password=#{password}, sex=#{sex}, birthday=#{birthday}
WHERE id = #{id} <!--方法参数为对象时,可直接使用#{属性名}进行获取-->
</update>
添加
标签:< insert id="" parameterType="" >
<!--手动主键-->
<insert id="insertUser" parameterType="user">
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#{birthday},NULL);
</insert>
<!--自动主键-->
<insert id="insertUser" parameterType="user">
<!-- 自动增长主键,以下两种方案均可 -->
INSERT INTO t_users VALUES(#{id},#{name},#{password},#{sex},#{birthday},NULL);
INSERT INTO t_users VALUES(NULL,#{name},#{password},#{sex},#{birthday},NULL);
</insert>
主键回填
标签:< selectKey id="" parameterType="" order="AFTER|BEFORE">
Mybatis工具类
-
Resource:用于获得读取配置文件的IO对象,耗费资源,建议通过IO一次性读取所有所需要的数据。
-
SqlSessionFactory:SqlSession工厂类,内存占用多,耗费资源,建议每个应用只创建一个对象。
-
SqlSession:相当于Connection,可控制事务,应为线程私有,不被多线程共享。
-
将获得连接、关闭连接、提交事务、回滚事务、获得接口实现类等方法进行封装
package com.qf.mybatis.part1.utils;
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 java.io.InputStream;
public class MyBatisUtils {
//获得SqlSession工厂
private static SqlSessionFactory factory;
//创建ThreadLocal绑定当前线程中的SqlSession对象
private static final ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();
static {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (Exception e) {
e.printStackTrace();
}
}
//获得连接(从tl中获得当前线程SqlSession)
private static SqlSession openSession(){
SqlSession session = tl.get();
if(session == null){
session = factory.openSession();
tl.set(session);
}
return session;
}
//释放连接(释放当前线程中的SqlSession)
private static void closeSession(){
SqlSession session = tl.get();
session.close();
tl.remove();
}
//提交事务(提交当前线程中的SqlSession所管理的事务)
public static void commit(){
SqlSession session = openSession();
session.commit();
closeSession();
}
//回滚事务(回滚当前线程中的SqlSession所管理的事务)
public static void rollback(){
SqlSession session = openSession();
session.rollback();
closeSession();
}
//获得接口实现类对象
public static <T extends Object> T getMapper(Class<T> clazz){
SqlSession session = openSession();
return session.getMapper(clazz);
}
}
ORM映射
Mybatis自动ORM失效
MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。
方案一:列的别名
<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
<select id="selectManagerByIdAndPwd" resultType="com.qf.mybatis.part2.orm.Manager">
SELECT mgr_id AS id , mgr_name AS username , mgr_pwd AS password
FROM t_managers
WHERE mgr_id = #{id} AND mgr_pwd = #{pwd}
</select>
</mapper>
方案二: 结果映射(ResultMap - 查询结果的封装规则)
<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
<!--定义resultMap标签-->
<resultMap id="managerResultMap" type="com.qf.mybatis.part2.orm.Manager">
<!--关联主键与列名-->
<id property="id" column="mgr_id" />
<!--关联属性与列名-->
<result property="username" column="mgr_name" />
<result property="password" column="mgr_pwd" />
</resultMap>
<!--使用resultMap作为ORM映射依据-->
<select id="selectAllManagers" resultMap="managerResultMap">
SELECT mgr_id , mgr_name , mgr_pwd
FROM t_managers
</select>
</mapper>
Mybatis处理关联关系-多表联查
实体间的关系:关联关系(拥有 has、属于 belong)
* OneToOne:一对一关系(Passenger--- Passport)
<mapper namespace="com.qf.mybatis.part2.one2one.PassengerDao">
<!-- 结果映射(查询结果的封装规则) -->
<resultMap id="passengerResultMap" type="com.qf.mybatis.part2.one2one.Passenger">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="sex" column="sex" />
<result property="birthday" column="birthday" />
<!-- 关系表中数据的封装规则 --> <!-- 指定关系表的实体类型 -->
<association property="passport" javaType="com.qf.mybatis.part2.one2one.Passport">
<id property="id" column="passport_id" />
<result property="nationality" column="nationality" />
<result property="expire" column="expire" />
<result property="passenger_id" column="passenger_id" />
</association>
</resultMap>
<!-- 多表连接查询 --> <!-- 结果映射(查询结果的封装规则)-->
<select id="selectPassengerById" resultMap="passengerResultMap">
<!-- 别名(避免与p1.id冲突) -->
SELECT p1.id , p1.name , p1.sex , p1.birthday , p2.id as passport_id , p2.nationality , p2.expire , p2.passenger_id
FROM t_passengers p1 LEFT JOIN t_passports p2
ON p1.id = p2.passenger_id
WHERE p1.id = #{id}
</select>
</mapper>
* OneToMany:一对多关系(Employee --- Department)
<mapper namespace="com.qf.mybatis.part2.one2many.DepartmentDao">
<!-- 封装规则 -->
<resultMap id="departmentResultMap" type="com.qf.mybatis.part2.one2many.Department">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="location" column="location" />
<!-- 关系表中数据的封装规则 --> <!-- 指定关系表的实体类型 -->
<collection property="emps" ofType="com.qf.mybatis.part2.one2many.Employee">
<id property="id" column="emp_id" />
<result property="name" column="emp_name" />
<result property="salary" column="salary" />
<result property="dept_id" column="dept_id" />
</collection>
</resultMap>
<!-- 多表连接查询 --> <!-- 封装规则 -->
<select id="selectDepartmentById" resultMap="departmentResultMap" >
<!-- 别名(避免与d.id、d.name冲突)-->
SELECT d.id , d.name , d.location , e.id AS emp_id , e.name emp_name , e.salary , e.dept_id
FROM t_departments d LEFT JOIN t_employees e
ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
</mapper>
* ManyToMany:多对多关系(Student --- Subject)
<mapper namespace="com.qf.mybatis.part2.many2many.StudentDao">
<!-- 映射查询只封装两表中的信息,可忽略关系表内容 -->
<resultMap id="allMap" type="com.qf.mybatis.part2.many2many.Student">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<collection property="subjects" ofType="com.qf.mybatis.part2.many2many.Subject">
<id property="id" column="sid" />
<result property="name" column="sname" />
<result property="grade" column="grade" />
</collection>
</resultMap>
<!-- 三表连接查询 -->
<select id="selectAllStudents" resultMap="allMap">
SELECT s1.* , ss.* , s2.id as sid , s2.name as sname , s2.grade
FROM t_students s1 LEFT JOIN t_stu_sub ss
ON s1.id = ss.student_id <!-- 通过t_stu_sub表建立二者之间的关系 -->
LEFT JOIN t_subjects s2
ON ss.subject_id = s2.id
</select>
</mapper>
关系总结
一方,添加集合;多方,添加对象。
双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。
持有对象关系属性,使用< association property="dept" javaType="department" >
持有集合关系属性,使用< collection property="emps" ofType="employee" >
动态SQL
1.<SQL>
<mapper namespace="com.qf.mybatis.part2.dynamic.BookDao">
<sql id="BOOKS_FIELD"> <!-- 定义SQL片段 -->
SELECT id,name,author,publish,sort
</sql>
<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
<include refid="BOOKS_FIELD" /> <!-- 通过ID引用SQL片段 -->
FROM t_books
</select>
</mapper>
<if>
<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
<include refid="BOOKS_FIELD" /> <!-- 通过ID引用SQL片段 -->
FROM t_books
<if test="name!=null">
name=#{name}
</if>
<if test="author!=null">
and author=#{author}
</if>
</select>
<where>
<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
SELECT id , name , author , publish , sort
FROM t_books
<where>
<if test="id != null"> <!-- WHERE,会自动忽略前后缀(如:and | or) -->
id = #{id}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="author != null">
and author = #{author}
</if>
<if test="publish != null">
and publish = #{publish}
</if>
<if test="sort != null">
and sort = #{sort}
</if>
</where>
</select>
<set>
<update id="updateBookByCondition">
UPDATE t_books
<set>
<if test="name != null"><!-- where子句中满足条件的if,会自动忽略后缀(如:,) -->
name = #{name} ,
</if>
<if test="author != null">
author = #{author} ,
</if>
<if test="publish != null">
publish = #{publish} ,
</if>
<if test="sort != null">
sort = #{sort} ,
</if>
</set>
WHERE id = #{id}
</update>
<trim>
<select id="selectBookByCondition" resultType="com.qf.mybatis.day2.dynamic.Book">
SELECT id,name,author,publish,sort
FROM t_books
<trim prefix="WHERE" prefixOverrides="AND|OR"> <!-- 增加WHERE前缀,自动忽略前缀 -->
<if test="id != null">
and id = #{id}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="author != null">
and author = #{author}
</if>
<if test="publish != null">
and publish = #{publish}
</if>
<if test="sort != null">
and sort = #{sort}
</if>
</trim>
</select>
<update id="updateBookByCondition">
UPDATE t_books
<trim prefix="SET" suffixOverrides=","> <!-- 增加SET前缀,自动忽略后缀 -->
<if test="name != null">
name = #{name} ,
</if>
<if test="author != null">
author = #{author} ,
</if>
<if test="publish != null">
publish = #{publish} ,
</if>
<if test="sort != null">
sort = #{sort}
</if>
</trim>
WHERE id = #{id}
</update>
<foreach>
<delete id="deleteBookByIds">
DELETE FROM t_books
WHERE id IN
<foreach collection="list" open="(" separator="," close=")" item="id" index="i">
#{id}
</foreach>
</delete>
参数 | 描述 | 取值 |
---|---|---|
collection | 容器类型 | list、array、map |
open | 起始符 | ( |
close | 结束符 | ) |
separator | 分隔符 | , |
index | 下标号 | 从0开始,依次递增 |
item | 当前项 | 任意名称(循环中通过 #{任意名称} 表达式访问) |