mybatis预习博客
一、概述
1.1、框架是什么
1.1.1 框架定义
框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种认为,框架是可被应用开发者定制的应用骨架、模板。
简单的说,框架其实是半成品软件,就是一组组件,供你使用完成你自己的系统。从另一个角度来说框架一个舞台,你在舞台上做表演。在框架基础上加入你要完成的功能。
框架安全的,可复用的,不断升级的软件。
1.1.2框架解决的问题
框架要解决的最重要的一个问题是技术整合,在 J2EE 的 框架中,有着各种各样的技术,不同的应用,系统使用不同的技术解决问题。需要从 J2EE 中选择不同的技术,而技术自身的复杂性,有导致更大的风险。企业在开发软件项目时,主要目的是解决业务问题。 即要求企业负责技术本身,又要求解决业务问题。这是大多数企业不能完成的。框架把相关的技术融合在一起,企业开发可以集中在业务领域方面。
另一个方面可以提供开发的效率。
1.2 MyBatis框架
1.2.1、什么是mybatis框架
- mybatis框架就是一个封装jdbc的持久层框架,它和hibernate都属于ORM框架,但是,hibernate是一个完整的orm框架,mybatis是不完全的mybatis框架
- mybatis框架让程序员只关注sql本身,而不需要去关注如连接的创建,statement的创建等操作
- mybatis会将输入的参数输出的结果进行映射
1.2.2、MyBatis框架的结构
二、mybatis快速入门
2.1、入门案例
- 创建mysql数据库和表
#创建数据库ssm
CREATE DATABASE ssm DEFAULT CHARSET utf8;
#使用(打开)ssm数据库
use ssm;
#创建表student
CREATE TABLE `student` (
`id` int(11) AUTO_INCREMENT primary key ,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into student(name,email,age) values('张三','zhangsan@126.com',22);
insert into student(name,email,age) values('李四','lisi@126.com',21);
insert into student(name,email,age) values('王五','wangwu@163.com',22);
insert into student(name,email,age) values('赵六','zhaoliun@qq.com',24);
select * from student;
- 创建maven工程,添加依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.23</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
- 编写student实体类
public class Student {
private int id;
private String name;
private String email;
private int age;
}
- 在resources下添加jdbc,properties文件
- 创建,编写mybatis核心配置文件(SqlMapConfig.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>
<!-- 读取属性文件(jdbc.properties
属性:
resource:从resources目录下找到指定名称的文件加载
url:使用绝对路径加载属性文件
-->
<properties resource="jdbc.properties"></properties>
<!--设置日志,输出底层执行的代码-->
<settings>
<!--值是固定的-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--给实体类注册别名-->
<typeAliases>
<!--单个注册-->
<!--<typeAlias type="com.bluemsun.entity.Student" alias="student"></typeAlias>-->
<!--批量注册(别名就是实体类类名的驼峰命名)-->
<package name="com.bluemsun.entity"/>
</typeAliases>
<!-- 配置数据库的环境变量(数据库连接配置)-->
<environments default="development">
<environment id="development">
<!-- 配置事务管理器
type:指定事务的管理方式
JDBC:事务控制交给程序员处理
MANAGED:由容器(spring)来管理事务
-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源
type:指定不同的配置方式
JNDI:java命名目录接口,在服务器端进行数据库连接池的管理
POOLED:使用数据库连接池
UNPOOLED:不使用数据库连接池
-->
<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>
<!-- 注册mapper.xml文件-->
</configuration>
- 创建,编写StudentMapper.xml文件(该文件完成数据库中student表的所有增删改查的操作.)
<?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">
<!--
mapper:是整个文件的大标签,用来开始和结束xml文件
属性:
namespace:指定命名空间(相当于包名),用来区分不同的mapper.xml
文件中相同的id属性
-->
<mapper namespace="dcs">
<!--实现查询功能的select子标签-->
<!--
resultType:指定查询返回的结果集的类型,如果是集合则必须是泛型
parameterType:如果有参数,则通过它来指定参数类型
-->
<!--查询所有-->
<select id="getAll" resultType="student">
select id,name,email,age from student
</select>
<!--通过id查询部分-->
<select id="getById" parameterType="int" resultType="student">
select id,name,email,age from student where id=#{id}
</select>
<!--模糊查询-->
<select id="getByName" parameterType="string" resultType="student">
select id,name,email,age
from student
<!--
where name like '%${name}%'
上面这种写法报错,因为JAVA反射只能获取方法参数的类型,但无从得知方法参数的名字的
_parameter则是java对通过反射获取参数后,给参数取的别名。
3.5.1及以下的版本,模糊查询时,如果入参是简单类型和String要用{_parameter}或者{value}
3.5.1以上的版本,如果入参是简单类型或String,花括号中随便写
-->
where name like '%${value}%'
</select>
<!--增加学生-->
<insert id="insert" parameterType="student">
insert into student (name,email,age) values (#{name},#{email},#{age})
</insert>
<!--按主键删除学生-->
<delete id="delete" parameterType="int">
delete from student where id = #{id};
</delete>
<!--更新-->
<update id="update" parameterType="student">
update student set name = #{name},email = #{email},age = #{age}
where id = #{id}
</update>
</mapper>
- 在主配置文件SqlMapConfig.xml文件中注册mapper.xml文件
<!-- 注册mapper.xml文件-->
<mappers>
<mapper resource="StudentMapper.xml"></mapper>
</mappers>
- 测试
public class MyTest {
SqlSession sqlSession;
//会在所有test执行前执行
@Before
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//取出sqlSession的对象
sqlSession = factory.openSession();
}
//test执行后执行
@After
public void close(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void selectTest01() throws IOException {
//完成查询操作
List<Student> list = sqlSession.selectList("dcs.getAll");
list.forEach(student -> System.out.println(student));
}
@Test
public void selectTest02() throws IOException {
//完成查询操作
Student student = sqlSession.selectOne("dcs.getById",2);
System.out.println(student);
}
@Test
public void selectTest03() throws IOException {
//完成查询
List<Student> list = sqlSession.selectList("dcs.getByName","李");
list.forEach(student -> System.out.println(student));
}
@Test
public void insertTest01() throws IOException{
Student stu = new Student("dcs","dcs@qq.com",19);
int result = sqlSession.insert("dcs.insert", stu);
System.out.println(result);
sqlSession.commit(); //手动提交事务
}
@Test
public void deleteTest01() throws IOException{
int result = sqlSession.delete("dcs.delete", 7);
System.out.println(result);
sqlSession.commit(); //手动提交事务
}
@Test
public void updateTest01() throws IOException{
int result = sqlSession.update("dcs.update", new Student(1,"zhangsan","zhangsan@123.com",30));
System.out.println(result);
sqlSession.commit(); //手动提交事务
}
}
2.2、MyBatis对象分析
(1)Resources 类
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。
(2)SqlSessionFactoryBuilder 类
SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于SqlSessionFactoryBuilder对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 对象创建为一个方法内的局部对象,方法结束,对象销毁。
(3)SqlSessionFactory 接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
A.openSession(true):创建一个有自动提交功能的 SqlSession
B.openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
C.openSession():同 openSession(false)
(4)SqlSession 接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。
SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。
三、动态代理
3.1、动态代理开发规范
MyBatis框架使用动态代理的方式来进行数据库的访问.
Mapper接口的开发相当于是过去的Dao接口的开发。由MyBatis框架根据接口定义创建动态代理对象,代理对象的方法体同Dao接口实现类的方法。在设计时要遵守以下规范.
- Mapper接口与Mapper.xml文件在同一个目录下
- Mapper接口的完全限定名与Mapper.xml文件中的namespace的值相同。
- Mapper接口方法名称与Mapper.xml中的标签的statement 的ID完全相同。
- Mapper接口方法的输入参数类型与Mapper.xml的每个sql的parameterType的类 型相同
- Mapper接口方法的输出参数与Mapper.xml的每个sql的resultType的类型相同。
- Mapper文件中的namespace的值是接口的完全限定名称.
- 在SqlMapConfig.xml文件中注册时,使用class属性=接口的完全限定名.
3.2、开发步骤
- 新建项目,添加依赖
- 加入jdbc.properties属性配置文件
- 编写实体类
- 创建,编写环境配置文件(SqlMapConfig.xml)
- 创建,编写Mapper接口
public interface UsersMapper {
//查询所有
List<Users> getAll();
//更新数据
int updateById(Users users);
//优化的模糊查询(不用${},没有sql注入问题)
List<Users> getByName(String username);
//模糊查询名字或住址
List<Users> getByNameAddr(@Param("colName") String colName, @Param("colValue") String colValue);
}
- 创建,编写Mapper.xml文件(与Mapper接口重名,后缀不同)
<?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">
<mapper namespace="com.bluemsun.mapper.UsersMapper">
<!--查询所有-->
<select id="getAll" resultType="users">
select id,username,birthday,sex,address
from users
</select>
<!--根据id更新一条数据-->
<update id="updateById" parameterType="users">
update users
set username = #{userName},birthday = #{birthday},sex = #{sex},address = #{address}
where id = #{id}
</update>
<!--优化后的模糊查询-->
<select id="getByName" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like concat('%',#{username},'%')
</select>
<!--模糊查询名字或者地址-->
<!--
${}可以进行字符串替换
如果入参有多个,那么就不写parameterType属性
-->
<select id="getByNameAddr" resultType="users">
select id,username,birthday,sex,address
from users
where ${colName} like concat('%',#{colValue},'%')
</select>
</mapper>
- 注册mapper.xml文件
<!--注册mapper.xml文件-->
<mappers>
<!--<mapper class="com.bluemsun.mapper.UsersMapper"></mapper>-->
<!--批量注册-->
<package name="com.bluemsun.mapper"></package>
</mappers>
- 测试
public class Mytest {
SqlSession sqlSession;
//动态代理对象
UsersMapper uMapper;
//日期格式化刷子
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@Before
public void openSqlSession() throws IOException {
//读取核心配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//获取sqlSession对象
sqlSession = factory.openSession();
//获取动态代理对象
uMapper = sqlSession.getMapper(UsersMapper.class);
}
@After
public void closeSqlSession() throws IOException{
sqlSession.close();
}
@Test
public void selectTest01(){
//调用接口中的方法
List<Users> list = uMapper.getAll();
list.forEach(users -> System.out.println(users));
}
@Test
public void selectByName(){
List<Users> list = uMapper.getByName("张");
list.forEach(users -> System.out.println(users));
}
@Test
public void selectByNameAddr(){
List<Users> list = uMapper.getByNameAddr("sex","1");
list.forEach(users -> System.out.println(users));
}
@Test
public void updateTest01() throws ParseException {
int result = uMapper.updateById(new Users(7,"dcs",sdf.parse("2002-12-2"),'1',"重庆奉节"));
System.out.println(result);
//切记:要手动提交事务
sqlSession.commit();
}
}
四、动态sql
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
4.1、< sql>标签
当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。
<!--所有列名,查找users表的所有列的数据时可以引用-->
<sql id="allCols">
id,username,birthday,sex,address
</sql>
4.2、< include>标签
用于引用定义的常量。
<!--查询所有-->
<select id="getAll" resultType="users">
<!--
等同于:
select id,username,birthday,sex,address
from users
-->
select <include refid="allCols"></include>
from users
</select>
4.3、< if>和< where>标签
< if>进行条件判断。
< where> 进行多条件拼接,在查询,删除,更新中使用.
<!--通过动态sql查询过-->
<select id="getByDynamicSql" resultType="users" parameterType="users">
select <include refid="allCols"></include>
from users
<where>
<if test="userName != null and userName != ''">
and username like concat('%',#{userName},'%')
</if>
<if test="birthday != null">
and birthday = #{birthday}
</if>
<if test="sex != '' and sex != null">
and sex = #{sex}
</if>
<if test="address != null and address != ''">
and address like concat('%',#{address},'%')
</if>
</where>
</select>
4.3、< set>标签
有选择的进行更新处理,至少更新一列.能够保证如果没有传值进来,则数据库中的数据保持不变.
<!--使用动态sql更新数据-->
<update id="updateByDynamicSql" parameterType="users">
update users
<set>
<if test="userName != null and userName != ''">
username = #{userName},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex != '' and sex != null">
sex = #{sex},
</if>
<if test="address != null and address != ''">
address = #{address},
</if>
</set>
where id = #{id}
</update>
4.4、< choose>标签
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
<!--测试choose和when-->
<select id="getByChoose" resultType="users" parameterType="users">
select <include refid="allCol"></include>
from users
<where>
<choose>
<when test="userName != null and userName != ''">
and username like concat('%',#{userName},'%')
</when>
<when test="sex != null and sex != ''">
and sex = #{sex}
</when>
<otherwise>
and address like concat('%',#{address},'%')
</otherwise>
</choose>
</where>
</select>
4.5、< foreach>标签
< foreach>主要用来进行集合或数组的遍历,主要有以下参数:
collection:collection 属性的值有三个分别是 list、array、map 三种,分别对应的参数类型为:List、数组、map 集合。
item :循环体中的具体对象。支持属性的点路径访问,如item.age,item.info.details,在list和数组中是其中的对象,在map中是value。
index :在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
open :表示该语句以什么开始
close :表示该语句以什么结束
separator :表示元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
<!--foreach-->
<select id="getByForeach" resultType="users">
select <include refid="allCols"></include>
from users
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
五、表的关联关系
我们通常说的关联关系有以下四种,一对多关联,多对一关联,一对一关联,多对多关联。关联关系是有方向的。如果是高并发的场景中,不适合做表的关联。
5.1、一对多(customer --> order)
在一对多关联关系中,一方(客户)中有多方(订单)的集合 ,所以要使用<collection>标签来映射多方的属性。
核心代码:
实体类:
Customer --> Order 一对多
order --> Customer 多对一
//客户
public class Customer {
private int id;
private String name;
private int age;
//该用户下的所有订单
List<Order> orders;
}
//订单
public class Order {
private int id;
private String orderNumber;
private double orderPrice;
//下这个订单的用户
private Customer customer;
}
接口:
public interface CustomerMapper {
//查询所有(方案一)
List<Customer> getAll();
//查询所有(方案二)
List<Customer> getAll2();
//查询所有(方案三)
List<Customer> getAll3();
}
CustomerMapper.xml文件
方案一:编写表关联查询得sql语句,进行结果集映射
<!--方法一:表的关联查询,起别名-->
<resultMap id="customerMap" type="customer">
<!--主键-->
<id property="id" column="cid"></id>
<!--非主键-->
<result property="name" column="cname"></result>
<result property="age" column="cage"></result>
<!--其他属性-->
<collection property="orders" ofType="order">
<id property="id" column="oid"></id>
<result property="orderNumber" column="onum"></result>
<result property="orderPrice" column="oprice"></result>
</collection>
</resultMap>
<select id="getAll" resultMap="customerMap">
select c.id cid,c.name cname,c.age cage,o.id oid,o.orderNumber onum,o.orderPrice oprice
from customer c left join orders o on c.id = o.customer_id
</select>
方案二:嵌套查询(在CustomerMapper.xml文件中编写查询orders表的sql)
<!--
方法二:使用嵌套查询的方式,不用表关联查询
瑕疵:在CustomerMapper.xml文件中编写了对orders表的sql语句
-->
<resultMap id="customerMap2" type="customer">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<!--column是当前客户表的id,传给嵌套查询作为入参进行查询返回该客户下的所有订单集合-->
<collection property="orders" column="id" ofType="order"
select="getOrderByCustomerId"></collection>
</resultMap>
<select id="getAll2" resultMap="customerMap2">
select id,name,age from customer
</select>
<!--嵌套查询,查询orders表-->
<select id="getOrderByCustomerId" resultType="order" parameterType="int">
select id,orderNumber,orderPrice from orders where customer_id = #{id}
</select>
方案三:嵌套查询(每个mapper里只有自己的增删改查)
CustomerMapper.xml文件
<!--方法三:把对订单的嵌套查询语句写在OrderMapper.xml文件中-->
<resultMap id="customerMap3" type="customer">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<!--column是当前客户表的id,传给嵌套查询作为入参进行查询返回该客户下的所有订单集合-->
<collection property="orders" column="id" ofType="order"
select="com.bluemsun.mapper.OrderMapper.selectOrderByCustomerId">
</collection>
</resultMap>
<select id="getAll3" resultMap="customerMap3">
select id,name,age from customer
</select>
OrderMapper.xml文件
<!--嵌套查询,供查询某客户下的所有订单-->
<select id="selectOrderByCustomerId" resultType="order" parameterType="int">
select id,orderNumber,orderPrice from orders where customer_id = #{id}
</select>
5.2、多对一(order --> customer)
在多对一关联关系中,多方(订单)中持有一方(客户)的对象,要使用标签<association>标签来映射一方的属性。
核心代码:
实体类:(和上面一对多一样)
接口:
public interface OrderMapper {
//查询所有(嵌套查询)
List<Order> getAll();
}
OrderMapper.xml文件
OrderMapper.xml
<!--查询所有:嵌套查询-->
<resultMap id="orderMap" type="order">
<id property="id" column="id"></id>
<result property="orderNumber" column="orderNumber"></result>
<result property="orderPrice" column="orderPrice"></result>
<association property="customer" column="customer_id"
select="com.bluemsun.mapper.CustomerMapper.selectCumtomerByOrderId">
</association>
</resultMap>
<select id="getAll" resultMap="orderMap">
select id,orderNumber,orderPrice,customer_id from orders
</select>
CustomerMapper.xml
<!--嵌套查询,供查找某个订单对应的客户-->
<select id="selectCumtomerByOrderId" resultType="customer" parameterType="int">
select id,name,age from customer where id = #{id}
</select>