一、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>### 提取工具类```javapackage 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 {@Overridepublic List<User> findAll() {try {// 1.加载核心配置文件InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");// 2.构建工厂SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);// 3.创建会话SqlSession sqlSession = sqlSessionFactory.openSession();// 4.执行sqlList<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 {// 模拟servicepublic 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>
