一、MyBatis快速入门
- MyBatis概述
- MyBaits是一个半自动化的持久层框架
- MyBatis封装了jdbc的很多细节,开发者只需要关注sql本身,无需关注注册驱动,获取连接等操作
- MyBatis使用ORM思想来对结果集封装
ORM
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>
<!--配置数据库环境-->
<environments default="mysql">
<environment id="mysql">
<!--事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--使用默认的数据库的连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="com/smiledog/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
实体类的配置文件为:实体类名+Mapper.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">
<mapper namespace="UserMapper">
<!--查询所有用户信息-->
<select id="findAllUsers" resultType="com.itfxp.domain.User">
select * from user
</select>
</mapper>
测试代码
- 加载核心配置文件
- 构建sqlSessionFactor工厂对象
- 通过工厂创建sqlSession会话对象
执行sql语句
public class MyBatisTest {
public static void main(String[] args) throws IOException {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 构建SqlSessionFactory工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 通过工厂创建SqlSession会话对象
SqlSession sqlSession = sessionFactory.openSession();
// 执行sql语句
List<User> list = sqlSession.selectList("UserMapper.findAllUsers");
for (User user : list) {
System.out.println(user);
}
// 释放资源
sqlSession.close();
is.close();
}
}
2、MyBatis增删改查
添加数据
UserMapper配置文件
<insert id="addUser" parameterType="com.smiledog.domain.User">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
测试代码
private static void addUser() throws IOException {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 构建SqlSessionFactory工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 通过工厂创建SqlSession会话对象
SqlSession sqlSession = sessionFactory.openSession();
User user = new User();
user.setUsername("王朝马汉");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("河南开封");
int insert = sqlSession.insert("user.addUser", user);
System.out.println(insert);
// 手动提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
is.close();
}
注:
1、插入语句Insert标签
2、在影射文件找那个使用parameterType属性指定插入数据类型
3、sql语句#{实体属性名} 表示?占位符
4、插入操作的API是sqlSession.instert(“命名空间.id”,实体对象);
5、DML类型语句mybatis需要手动提交事务 sqlSession.commit( );修改数据
UserMapper配置文件
<update id="updateUser" parameterType="com.smiledog.domain.User">
update user set username= #{username}, birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}
</update>
测试代码
private static void updateUser() throws IOException {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 构建SqlSessionFactory工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 通过工厂创建SqlSession会话对象
SqlSession sqlSession = sessionFactory.openSession();
User user = new User();
user.setUsername("张三");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("河南开封");
user.setId(50);
int update = sqlSession.update("user.updateUser", user);
System.out.println(update);
// 手动提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
is.close();
}
删除数据
UserMapper配置文件
<delete id="delUser" parameterType="int">
delete from user where id = #{id}
</delete>
测试代码
```java private static void delUser() throws IOException { // 加载核心配置文件 InputStream is = Resources.getResourceAsStream(“SqlMapConfig.xml”);
// 构建SqlSessionFactory工厂对象 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 通过工厂创建SqlSession会话对象 SqlSession sqlSession = sessionFactory.openSession();
int del = sqlSession.delete("user.delUser", 50);
System.out.println(del);
// 手动提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
is.close();
}
<a name="0Mfm7"></a>
### 提取工具类
```java
package com.yunhe.mybatis.utils;
/*
@ClassName SqlSessionFactoryUtil
@Date 2021/6/23
@Time 16:55
*/
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.IOException;
import java.io.InputStream;
public class SqlSessionFactoryUtil {
public SqlSessionFactoryUtil() {
}
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
//加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 构建SqlSessionFactory工厂对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession openSession(){
// 通过工厂创建SqlSession会话对象
return sqlSessionFactory.openSession();
}
public static void close (SqlSession sqlSession){
sqlSession.commit(); //提交事务
sqlSession.close(); //释放资源
}
}
3、MyBatis常用标签
核心配置文件的层级关系:
Configuration
- properties(属性)
- setting(设置)
- typeAliases(类型别名)
- objectFactory(对象工厂)
- plugis(插件)
- environments(环境配置)
- environment(环境变量)
- transationManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseldProvider(数据库厂商标识)
-
environments:
properties:
typeAliases:
为Java类型设置一个短的名字(类型别名),mybatis框架内置了一些java类型的别名
<typeAliases>
<!--这种方式只能设置一个别名,如果有其他的,还得再写一个,这种方式不推荐-->
<!--<typeAlias type="com.smiledog.domain.User" alias="User"></typeAlias>-->
<!--将当前的包下的类名,都设置了别名,别名是:当前类的名字-->
<package name="com.smiledog.domain"></package>
</typeAliases>
mappers:
用于记载映射文件, ```java 基本使用方法:
加载指定接口的全限定名 ==注解开发时使用==
加载并扫描指定包下所有的接口 ==基于接口扫描方式加载==
<a name="md0Lk"></a>
## 4、MyBatis的API
<a name="0ZGQx"></a>
#### Resource:
加载mybatis的核心配置文件
```java
// 加载mybatis的核心配置文件,获取io流
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//也可以使用类加载器(ClassLoader)加载配置文件
SqlSessionFactoryBuilder:
根据mybatis的核心配置文件构建出SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlsessionFactory:
用于创建SqlSession会话对象(同Connection对象)
这是一个工厂对象,对于这种创建和销毁都非常消耗资源,一个项目中只需要存在一个即可
// DML类型语句,需要手动提交事务
SqlSession openSession();
// 设置是否开启自动提交事务的会话对象,如果设置true,自动提交【了解】
SqlSession openSession(boolean autoCommit);
一般不管这个,默认手动提交
SqlSession:
这是MyBatis的一个核心对象,我们基于这个对象可以实现对数据的CRUD操作
对于这个对象应做到每个线程多,每次用时打开,用完关闭
- <T> T selectOne(String statement, Object parameter);
- <E> List<E> selectList(String statement, Object parameter);
- int insert(String statement, Object parameter);
- int update(String statement, Object parameter);
- int delete(String statement, Object parameter);
- void commit();
- void roolback();
二、MyBatis的实现
1、MyBatis实现Dao层
a、传统的Dao层实现方式
UserMapper接口:
public interface UserMapper {
// 查询所有
public List<User> findAll();
}
UserMapper实现类:
public class UserMapperImpl implements UserMapper {
@Override
public List<User> findAll() {
try {
// 1.加载核心配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2.构建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
// 3.创建会话
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.执行sql
List<User> list = sqlSession.selectList("UserMapper.findAll");
// 5.释放资源
sqlSession.close();
return list;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
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="UserMapper">
<!--查询所有-->
<select id="findAll" resultType="User">
select * from user
</select>
</mapper>
实现service的调用:
public class UserMapperTest {
// 模拟service
public static void main(String[] args){
// 调用dao层代码
UserMapper userMapper = new UserMapperImpl();
List<User> list = userMapper.findAll();
System.out.println(list);
}
}
b、MyBatis接口代理模式
特别之处:
1、在MyBatis 的核心配置文件SqlMapConfig中配置Mappers
<mappers>
<!--动态获取,基于接口扫描进行加载mapper配置文件-->
<!--路径为mapper配置文件所在包路径-->
<package name="com.smiledog.mybatis.mapper"/>
</mappers>
2、在使用中获取核心配置文件中获取接口实现类的代理对象,通过接口实现类对象调用方法
public class MyBatisDaoTest {
public static void main(String[] args) throws IOException {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 构建SqlSessionFactory工厂对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 通过工厂创建SqlSession会话对象
SqlSession sqlSession = sessionFactory.openSession();
// 获取接口的实现类的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> allUser = mapper.findAllUser();
for (User user : allUser) {
System.out.println(user);
}
}
}
c、MyBatis接口开发规范
- Mapper映射文件的namespace与Mapper接口全限定名一致
- Mapper接口的方法名与id的属性名一致
- 方法的参数类型与parameter属性类型一致
- 方法的返回值类型与resultType属性类型一致
- 映射文件需要与接口在同一个包下,文件名和接口名相同:扫描包,加载所有的映射文件:<package name = "com.smiledog.mapper" />
d、MyBatis增删改查
<?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.itfxp.mapper.UserMapper">
<!--查询所有用户信息-->
<select id="findAllUser" resultType="User">
select * from user
</select>
<!--新增-->
<insert id="save" parameterType="User">
insert into user (username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
<!--修改-->
<update id="update" parameterType="User">
update user set username = #{username},birthday = #{birthday},sex = #{sex},
address = #{address} where id = #{id}
</update>
<!--删除-->
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<!--查询一个-->
<select id="findById" parameterType="int" resultType="User">
select * from user where id = #{id}
</select>
</mapper>
2、MyBatis单表查询
resultMap:
在查询数据时进行结果封装,会出现别名和实体类的属性名不一直跟的情况,导致赋值赋不上,这个时候我们就要使用resultMap属性,进行手动建立字段映射关系
<!--
resultMap 手动建立映射
id="userResultMap"
type="com.smiledog.domain.User" 建立映射的java类型
id 标签 主键
column="uid" 列名
property="id" 实体属性名
result 标签 普通字段
column="name" 列名
property="username" 实体属性名
-->
<resultMap id="userResultMap" type="com.smiledog.domain.User">
<id column="uid" property="id"></id>
<result column="name" property="username"></result>
<result column="bir" property="birthday"></result>
<result column="gender" property="sex"></result>
<result column="address" property="address"></result>
</resultMap>
<!--查询所有用户信息-->
<select id="findAllUser" resultMap="userResultMap">
SELECT id AS uid, username AS `name`,birthday AS bir ,sex AS gender ,address FROM user
</select>
模糊查询:
方式一:不建议使用
<select id="findUserByLike1" parameterType="string" resultType="User">
select * from user where username like #{username}
</select>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 创建工厂获取Session代码省略
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findUserByLike1("%王%");
for (User user : users) {
System.out.println(user);
}
方式二:mysql5.5版本以前不支持多单引号拼接
oracle数据库,除了别名位置,其余位置能使用双引号
<select id="findUserByLike2" parameterType="string" resultType="User">
select * from user where username like "%" #{username} "%"
</select>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 创建工厂获取Session代码省略
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findUserByLike2("王");
for (User user : users) {
System.out.println(user);
}
方式三:会出现sql注入
${}字符串拼接,如果接收到的简单数据类型,表达式名称必须是value
<select id="findUserByLike3" parameterType="string" resultType="User">
select * from user where username like '%${value}%'
</select>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 创建工厂获取Session代码省略
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findUserByLike3("王");
for (User user : users) {
System.out.println(user);
}
方式四:使用concat函数拼接
<select id="findUserByLike4" parameterType="string" resultType="User">
select * from user where username like concat(concat('%',#{username}),'%')
</select>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 创建工厂获取Session代码省略
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findUserByLike4("王");
for (User user : users) {
System.out.println(user);
}
#{}与${}的区别
${}:底层Statement
- sql与拼接参数拼接在一起,会出现sql注入问题
- 每次执行sql语句都会编译一次
- 接收简单类型,命名:${value}
- 接收引用类型,命名:${属性名}
- 字符串类型需要加 '${value}'
#{}:底层PreparedStatement
- sql与参数分离,不会出现sql注入问题
- sql只需要编译一次
- 接收简单类型,命名:#{随便写}
- 接收引用类型,命名:#{属性名}
3、MyBatis映射文件
返回主键:
useGeneratedkeys属性
此方式只支持主键自增
<insert id="addUser" parameterType="User"
useGeneratedKeys="true"
keyColumn="id"
keyProperty="id">
insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
</insert>
selectKey标签
<insert id="addUser" parameterType="User">
<selectKey keyColumn="id" ==> 表中主键列
keyProperty="id" ==> 实体主键属性
resultType="int" ==> 实体类逐渐属性类型
order="AFTER"> ==> 表示此这个标签的sql语句实在insert语句之前执行(BEFORE),还是之后执行(AFTER)
SELECT LAST_INSERT_ID()
==>该函数是mysql提供的一个高级查询的函数,主要用于获取最后一次插入数据时的id
</selectKey>
insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
</insert>
动态SQL:
根据用户传入的参数不同,生成的sql语句结构也就不同,查询数据也不同,这就是动态SQL
if标签:
比如有两个参数:id和username
<!--
if标签 条件判断
where标签 相当于 where 1=1 功能,如果没有条件情况下 where语句不在sql语句拼接
可以去掉第一个 and 或者 or
-->
<select id="findByIdAndUsernameIf" parameterType="User" resultType="User">
select * from user
<where>
<if test="id != null">
and id= #{id}
</if>
<if test="username !=null">
or username = #{username}
</if>
</where>
</select>
set标签:
主要用于update更新数据
<!--
set标签 更新 ,将条件中的最后一个逗号抹除
-->
<update id="updateIf" parameterType="User">
update user
<set>
<if test="username !=null">
username = #{username} ,
</if>
<if test="birthday !=null">
birthday = #{birthday} ,
</if>
<if test="sex !=null">
sex = #{sex} ,
</if>
<if test="address != null">
address = #{address},
</if>
</set>
where id = #{id}
</update>
foreach标签:
相关属性:
- collection:代表要遍历的集合元素
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
多个id查询数据
<!--
foreach标签,普通list集合
传递 普通类型list集合 collection="list"
属性取值:collection、list
-->
<select id="findUsersForEacheList" parameterType="list" resultType="User">
select * from user where id in
<foreach collection="list" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!--
foreach标签,普通array数组
传统 普通类型array数组 collection="array"
属性取值 array
-->
<!--<select id="findUsersForEacheArray" parameterType="int" resultType="User">-->
<select id="findUsersForEacheArray" parameterType="int[]" resultType="User">
select * from user where id in
<foreach collection="array" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
SQL片段:
映射文件中可以将重复的sql提取出来,使用时用include引用即可
<!--
将当前映射文件的共同的sql代码抽取一个片段,实现sql的复用性...
id="selectUser" 当前sql片段的唯一标识
-->
<sql id="selectUser">
select id,username,birthday,sex,address from user
</sql>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<select id="findUsersByIds" parameterType="User" resultType="User">
<include refid="selectUser" /> where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>