目录
1、什么是MyBatis
MyBatis是一款 优秀的 ORM 持久层 框架,用于简化版JDBC开发
ORM
Object - Relational - Mapping
即 Java对象(类)- 关系型数据库(表)- 映射配置
用来映射 类名和表名、属性和字段、Java类型和数据库类型等信息
持久层
瞬时状态转为持久状态过程---包括 插入、更新、删除等操作
持久状态转为瞬时状态过程---包括 查询操作
持久化:瞬时状态和持久状态相互转化的过程,称之为持久化
负责将数据保存到数据库的那一层代码
JavaEE三层架构:表现层、业务层、持久层
序列化
ObjectinputStreame 和 ObjectoututStreame
把对象状态转换为可存储或可传输的状态,称之为序列化
把可存储或可传输的状态转换为对象状态,称之为反序列化
框架
框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
2、搭建MyBatis项目
准备工作:
创建数据库和表
Maven
传统 Web项目,把jar包手动拷贝到WEB-INF/lib
jar多
版本不一致
Maven Web项目,只需配置pom.xml即可,自动下载、导入和管理jar包
3、Maven
Maven 是一款优秀Java自动构建工具
源码 - 编译 - 测试 - 部署 - 安装等 到服务器
同类:Ant、Gradle
安装配置
-
下载:Maven官网
-
配置:
-
解压到非中文目录下
-
配置 Maven_Home 安装目录下的conf目录下的settings.xml配置文件,配置本地仓库的目录
-
例如本电脑:D:\maven\apache-maven-3.6.1\conf\settings.xml
-
本地仓库:存放bar包的位置目录
<localRepository>D:\maven\apache-maven-3.6.1\maven_repository</localRepository>
-
-
在 idea 开发工具中配置自定义的 Maven 目录
-
File - Settings - 搜索 Maven 进行配置,如下图:
-
注意:为了防止新建工程时,又重新恢复到默认,建议配置“新建项目设置”,可永久保存
-
配置阿里云镜像
问题:每次在下载第一次使用的jar时,都会去访问中央仓库 国外的服务器,速度相对较慢
所以为了提高下载速度,我们可以修改默认下载服务器的url,更换为阿里云服务器
-
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
-
-
-
新建Maven项目
File - New Project - Empty Project
新工程下右键,new Module 选择 Maven
注意:新的Maven目录
约定大于配置
源代码文件:src/main/java
资源文件位置:src/main/resources
测试代码目录:src/test/java
自动化和半自动化
自动化代表Hibernate,半自动化代表Mybatis
自动化的优点 开发效率高,代码量小,不需要学习SQL,缺点 缺失灵活性
半自动化优点 灵活性高运行效率相对较高,省略了自动生成SQL的过程,缺点 开发效率相对较低,代码量大,需要血虚SQL
4、MyBatis开发步骤
(1)配置依赖
在pom.xml中配置 mysql 驱动依赖和 mybatis 依赖
<dependencies> <!--mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <!--mybatis 依赖--> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> </dependency> </dependencies>
(2)新建和配置mybatis配置文件
可以通过官网拷贝mybatis – MyBatis 3 | 入门
位置:在 src/main/resources 下新建 mybatis_config.xml(名字可以自己定义)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="developmentA"> <environment id="developmentA"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/school"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> <!-- <environment id="developmentB"> <transactionManager type=""></transactionManager> <dataSource type=""></dataSource> </environment>--> </environments> <mappers> <mapper resource="mapper/SchoolMapper.xml"/> </mappers> </configuration>
(3)编写实体类
和表结构对应
可以使用 Lombok 简化 setter 和 getter
@Data
public class School {
private Integer scid;
private String scname;
private String address;
}
(4)配置映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.adada.dao.MySpace">
<select id="getCount">
select count(*) from school
</select>
</mapper>
注意:还要更新配置文件,配置关联
<mappers>
<mapper resource="mapper/SchoolMapper.xml"/>
</mappers>
(5)编写代码
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
public class TestMyBatis {
public static void main(String[] args) {
//解析 xml 配置和映射文件
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
try {
InputStream is = Resources.getResourceAsStream("mybatis_config.xml");
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory= builder.build(is);
//创建SqlSession
SqlSession sqlSession= sqlSessionFactory.openSession();
//执行SQL
Integer count = sqlSession.selectOne("com.adada.dao.MySpace.getCount");
System.out.println(">>>>>>"+count);
//关闭
sqlSession.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、实现CRUD
为了简化获取session,先封装工具类MyBatisUtil
public class MyBatisUtil { private static String configFile = "mybatis_config.xml"; private static SqlSessionFactoryBuilder sqlSessionFactoryBuilder; private static SqlSessionFactory sqlSessionFactory; static { sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream(configFile); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream); } /* * 默认是true */ public static SqlSession getSqlSession() { return getSqlSession(true); } public static SqlSession getSqlSession(boolean commit) { return sqlSessionFactory.openSession(commit); } public static void close(SqlSession session){ if (session!=null){ session.close(); } } }
(1)插入操作:保存学校
-
修改映射文件,新增<insert>节点
<!--insert--> <insert id="saveSchool" parameterType="com.adada.pojo.School"> insert into school(scid,scname,address)values (#{scid},#{scname},#{address}) </insert>
-
调用
public class TestInsert { public static void main(String[] args) { SqlSession session = MyBatisUtil.getSqlSession(); //封装实体类 School school = new School(); school.setScid(5); school.setScname("金台高中"); school.setAddress("群众路"); //保存 int rows = session.insert("saveSchool", school); System.out.println(rows); //关闭 MyBatisUtil.close(session); } }
-
注意:
为了方便调试,最好在执行过程中能显示打印执行的日志信息,可以通过配置文件,配置开启日志功能 -- 打印Sql语句
<settings> <!--日志配置--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
(2)根据编号查询学校
-
修改映射文件,新增查询节点
<!--select--> <!--根据主键查询明细--> <select id="getSchoolById" resultType="school"> select scid,scname,address from school where scname=#{scname} </select>
-
注意别名:为了简化实体类写法,可以在配置文件中配置别名
<typeAliases> <!--配置一个实体类的别名,且不区分大小写--> <!-- <typeAlias type="com.adada.pojo.School" alias="school"></typeAlias>--> <!--配置包下所有类别名,在映射文件中,默认在此包下载实体类--> <package name="com.adada.pojo"/> </typeAliases>
-
编写代码调试
public class TestSelect { public static void main(String[] args) { SqlSession session= MyBatisUtil.getSqlSession(); School school=session.selectOne("getSchoolById",2); System.out.println(school); MyBatisUtil.close(session); } }
(3)修改学校信息
修改映射文件,新增<update>标记元素
先查询 在修改
映射文件:
<!--根据用户编号,修改学校信息-->
<update id="updateSchool" parameterType="school">
update school set scname=#{scname},address=#{address}
where scid=#{scid}
</update>
代码:
public class TestUpdate {
public static void main(String[] args) {
SqlSession session= MyBatisUtil.getSqlSession();
School school=session.selectOne("getSchoolById",2);
school.setScid(2);
school.setAddress("新建路");
session.update("updateSchool",school);
}
}
(4)根据学校编号删除
-
修改映射文件,新增<delete>标记元素
<delete id="delSchool"> delete from school where scid=#{scid} </delete>
-
代码:
public class TestDelete { public static void main(String[] args) { SqlSession session= MyBatisUtil.getSqlSession(); int rows = session.delete("delSchool", 5); System.out.println(rows); MyBatisUtil.close(session); } }
(5)其他
useGeneratedKeys的用法
-
作用:插入时,自动获取主键
场景描述:
需要同时新增新的老师,并新增老师属于此学校,所以需要保存学校后,直接获取到老师编号
SqlSession session = MyBatisUtil.getSqlSession(); //新增老师同时新增学校 Teacher teacher=new Teacher(); teacher.setSname("李四"); //保存部门 session.insert("save",teacher); System.out.println(">>>>>老师编号:"+teacher.getSid()); //封装实体类 School school = new School(); // school.setScid(5); school.setScname("金台高中"); // school.setAddress("群众路"); school.setScid(teacher.getSid()); //保存 int rows = session.insert("saveSchool", school);
默认情况下,上面的老师编号为null。
解决办法:在映射文件的<insert>标记中添加两个必须的属性 useGeneratedKeys="true" keyProperty="sid"
<insert id="save" parameterType="teacher" useGeneratedKeys="true" keyProperty="sid" keyColumn="sid"> insert into teacher(sname) values (#{sname}) </insert>
这样,再次执行上面的代码,老师的编号就有值了
复用SQL
先把公共部分,提取到<sql>标记中,并指定id编号,如:
<!--复用Sql:公共的片段,单独编写到 Sql 元素中--> <sql id="schoolCols"> scid,scname,address </sql>
在需要使用公共部分的位置,引用<include>标记元素,如:
<select id="queryAll" resultType="school"> select <include refid="schoolCols"></include> from school </select>
6、${} 和 #{}
在映射文件中引用传参时,可以同时使用 ${} 和 #{}
例如:实现学校的多条件查询,可以根据学校的名字和地址来做查询
-
修改映射文件,新增下面代码:
<!--根据学校名字和地址 多条件查询--> <select id="querySchool" resultType="school"> select scid,scname,address from school where scname=#{scname} and address='${address}' </select>
注意:上面的${address}必须带单引号的
-
上面配置使用了 #{} 和 ${} 生成的SQL如下:
=> Preparing: select scid,scname,address from school where scname=? and address='渭工路' => Parameters: 新福园(String) <= Total: 0
区别:
${} 用来拼接SQL语句时,使用时注意对于字符型,手动添加单引号。缺点:不安全,可能引起SQL注入安全问题
#{} 先生成带占位符?的语句,再给占位符赋值。优点:类型处理自动判断识别,灵活且安全
在注意:
如果传入多个参数,除了可以使用实体类之外,还可以使用Map来传参
public static void main(String[] args) {
SqlSession session= MyBatisUtil.getSqlSession();
Map<String,Object> param=new HashMap<>();
param.put("scname","新福园");
param.put("address","渭工路");
List<School> list=session.selectList("querySchool",param);
list.forEach(System.out::println);
}
7、配置文件
配置文件中常用的元素:
configuration(配置)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
(1)properties(属性)
可以通过此标记中的属性引用外部的属性配置文件,键值对的属性资源文件。
引用之前,要先新建db.properties,并编辑好内容
新建db.properties文件
username=root
password=root
url=jdbc:mysql://127.0.0.1:3306/school
driver=com.mysql.cj.jdbc.Driver
在配置文件中的使用:
<properties resource="db.properties">
<!-- 用作默认值,如果属性文件中获取不到对应属性时,这里才会起作用,优先级低于属性文件的-->
<property name="password" value="root"/>
</properties>
注意:
在配置文件的属性设置中 <property name="password" value="root"/>优先级低于属性资源配置文件,所以我们一般把配置文件中的属性设置为默认值。
使用属性文件中的配置信息:
<environment id="developmentA">
<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>
(2)environment(环境变量)
在配置文件中
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(两种取值也就是 type="[JDBC|MANAGED]")
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
-
大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
8、Mapper接口方式
在多层架构开发中,我们常常按功能划分为控制层、业务层、Dao层
在Dao层中,一般都会设计接口来访问数据库
第一步:定义接口
IShoolDao接口
/*
* 接口---特殊的抽象类---高度抽象类---接口
* 抽象---复杂问题简单化---具体实现接口
* 接口---定义规范
* */
public interface ISchoolDao {
void save(School school);
void delete(School school);
void update(School school);
School get(Integer scid);
List<School> queryAll();
}
第二步:编写映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 1.要求:命名空间必须和dao的接口保持一致,取值只能是接口的完整类名
因为可以把这里的映射文件理解成Dao接口的实现类
-->
<mapper namespace="com.adada.dao.ISchoolDao">
<!-- 就是ISchoolDao接口中get方法的实现 -->
<select id="get" resultType="school">
select scid,scname,address from school where scid=#{scid}
</select>
<select id="queryAll" resultType="school">
select scid,scname,address from school
</select>
</mapper>
注意:
namespace命名空间取值,必须是接口的完整限定名(全类名)
编写sql的标记元素id取值必须和接口名要实现的方法名保持一致
标记元素id的实现不需要和接口完全一致,比如:可以实现部分sql方法,可以额外增加其他名字的sql标记
第三步:编写业务层代码
通过sqlSession.getMapper() 方式获取对应接口的代理对象
/*
* Mapper 代理接口方式实现
* */
public class SchoolService {
public static void main(String[] args) {
//获取sqlSession
SqlSession sqlSession= MyBatisUtil.getSqlSession();
//动态代理 动态根据代理实现生成对应的实现类代理对象
ISchoolDao dao=sqlSession.getMapper(ISchoolDao.class);
//根据编号查询
School school= dao.get(1);
//查询所有学校
dao.queryAll().forEach(System.out::println);
System.out.println(school);
}
}
9、结果映射
当查询的 SQL 中,出现了表字段名和实体类属性名不一致情况时,需要指定映射关系
常见方式:
使用 SQL 的 AS 别名方式
ResultMap 方式
普通属性映射配置
第一步:编写结果映射
<!-- 结果映射 -->
<resultMap id="schoolMap" type="school">
<id property="scid" column="scid"></id>
<result property="scname" column="scname"></result>
<!-- 如果属性名和字段名一致时,可以省略掉 -->
<!-- <result property="address" column="address"></result>-->
</resultMap>
第二步:<select> 查询中引用
<!-- 使用结果映射,就是把 resultType 换成 resultMap,注意两者不能同时使用-->
<select id="queryAll" resultMap="schoolMap">
select scid,scname,address from school
</select>
第三步:代码查询,检查不一致的属性是否能获取到值
//获取sqlSession
SqlSession sqlSession= MyBatisUtil.getSqlSession();
//动态代理 动态根据代理实现生成对应的实现类代理对象
ISchoolDao dao=sqlSession.getMapper(ISchoolDao.class);
//根据编号查询
School school= dao.get(1);
//查询所有学校
dao.queryAll().forEach(System.out::println);
特别注意
autoMapping
此属性属于映射文件中 resultMap 标记
如果设置这个属性,MyBatis将会为本结果映射开启或关闭自动映射
这个属性会覆盖全局属性 autoMappingBehavior。默认值:true
如果是false,只是映射在<resultMap>中配置的属性才会被映射
<resultMap id="schoolMap" type="school" autoMapping="true">
<id property="scid" column="scid"></id>
<result property="scname" column="scname"></result>
<!-- 如果属性名和字段名一致时,可以省略掉 -->
<!-- <result property="address" column="address"></result>-->
</resultMap>
如果要设置所有的结果映射中的行为策略为默认禁用自动映射,可以在配置文件中在配置下面的配置
autoMappingBehavior
此属性属于配置文件中 settings 标记下的配置
指定 MyBatis 应如何自动配置列到字段或属性。
NONE 表示禁用全部的自动配置
PARTIAL 只会自动映射没有定义嵌套结果映射的字段
FULL 会自动映射任何复杂的结果集
<settings>
<setting name="autoMappingBehavior" value="PARTIAL"/> <!-- NONE 禁用全部的自动配置,PARTIAL 默认-->
</settings>
注意:
如果配置了 autoMappingBehavior 为 NONE 时,会对其他没有使用结果映射的查询结果产生影响
对没有使用结果映射,而使用 resultType 的查询,会查询结果直接为null。
例如:
配置文件中配置:
<setting name="autoMappingBehavior" value="NONE"/>
映射文件中:
<resultMap id="schoolMap" type="school" autoMapping="true">
<id property="scid" column="scid"></id>
<result property="scname" column="scname"></result>
</resultMap>
<!-- 下面使用 resultType,代码调用查询结果返回的 school 对象为null -->
<select id="get" resultType="school">
select scid,scname,address from school where scid=#{scid}
</select>
<!-- 下面使用 resultMap,可以有正常的映射结果 -->
<select id="queryAll" resultMap="schoolMap">
select scid,scname,address from school
</select>
10、关联映射 ?????
通过一个对象的查询,可以很方便的自动关联查询其他对象
一对一
一对多
多对多
实现方式
实现方式:
1.内嵌查询:发起N+1查询
2.内嵌映射