Mybatis笔记(上)
mybatis简介
什么是MyBatis?
简化数据库操作的,但在这框架下我们还是要写sql语句的
Mybatis是一个持久层框架,用于简化数据库操作。它通过sql或者注解将Java对象与SQL语句映射,避免了手动设置参数和结果集处理的繁琐工作。
Mybatis简化了数据库操作,提供了灵活的SQL控制,适合需要精细管理sql的项目
核心特点:
①SQL映射
②简化数据库操作
③灵活性
④集成方便
基本实现
使用MyBatis的步骤:
1导入依赖
2准备实体类
3创建一个mapper接口,mapperxml文件
4准备mybatis的配置文件(连接数据库信息,mapper.xml位置)
5使用mybatis进行数据库的查询
代码举例:
Mapper接口
/**
* 规定数据库方法即可
*/
public interface EmployeeMapper {
//根据id查询员工信息
Employee queryById(Integer id);
//根据id删除员工
int deleteById(Integer id);
}
写在EmployeeMapper.xml文件中的
声明标签写sql语句 crud select insert update delete
每个标签对应接口的一个方法!方法的一个实现!
只有查询才有返回值: id=每个方法的名字 resultType=每个方法的返回结果
注意;Mapper接口中的方法不能重载,因为xml文件不能映射,xml是根据方法名识别的!
<select id="queryById" resultType="org.example.pojo.Employee">
<!-- #{empId}代表动态传入的参数,并且进行赋值!后面详细讲解 -->
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_id = #{empId}
</select>
<delete id="deleteById">
delete from t_emp where emp_id=#{id}
</delete>
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>
<settings>
<!-- 开启了 mybatis的日志输出,选择使用system进行控制台输出 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器
MANAGED 不会自动开启事务!
JDBC 自动开启事务,需要自己提交事务!(常用)
-->
<transactionManager type="JDBC"/>
<!-- 配置数据源 (一般我们会使用Druid连接池,不使用mybatis的)
POOLED mybatis帮助我们维护一个连接池
UNPOOlED 每次都新建或者释放连接
-->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="abc123LQ"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="mappers/EmployeeMapper.xml"/>
</mappers>
</configuration>
测试类:
public class MyBatisTest {
/**
* mybatis提供的api进行方法的调用
* 1 读取外部配置文件(mybatis-config.xml)
* 通过Resources 调用 getResourceAsStream()方法传入"mybatis-config.xml"-------获得一个输入流对象
*
* 2 创建sqlSessionFactory
* 通过new一个SqlSessionFactoryBuilder(),再调用build()方法,传入上面创建的输入流对象--------获得sqlSessionFactory对象
*
* 3 根据sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
* 通过上面创建的sqlSessionFactory对象调用openSession()方法-----------------拿到sqlSession对象
*
* 4 获取接口的代理对象(代理技术)调用代理对象的方法,就会查找mapper接口的方法
* 通过上面创建的sqlSession对象调用getMapper()方法传入EmployeeMapper.class(规定方法的的接口)--------得到一个代理对象
* 通过代理对象我们就可以调用接口中的方法,代理对象会自动找到对应的sql语句执行
*
* 5 提交事务(非DQL)和释放资源(对于sqlSession来说的)
*/
@Test
public void test1() throws IOException {
//1 读取外部配置文件(mybatis-config.xml)
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//2 创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//3 根据sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
SqlSession sqlSession = sqlSessionFactory.openSession();
//4 获取接口的代理对象(代理技术)调用代理对象的方法,就会查找mapper接口的方法
//jdk动态代理技术生成的mapper代理对象
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
//内部拼接接口的全限定符号.方法名 去查找sql语句标签
//1 拼接 类的权限限定符.方法名 整合参数 -》ibatis对应的方法传入参数
//mybatis底层依然调用ibatis只不过有固定的模式!
Employee employee = mapper.queryById(1);
System.out.println(employee);
//5 提交事务(非DQL)和释放资源
sqlSession.commit();
sqlSession.close();
}
}
日志输出设置
在mybatis-config.xml文件的setting标签中设置:
<!-- 开启了 mybatis的日志输出,选择使用system进行控制台输出 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
取值符#与$区别
#和$取值符号的区别
#{key}:占位符+赋值 emp_id=? -> ?=赋值
${key}:字符串拼接 "emp_id=" + id ->最终形成了一个sql语句
推荐使用#{key}防止出现注入攻击问题
特殊情况:
?只能替代值的位置,不能替代容器名(标签,列名,sql关键字)emp_id = ? 不能写成 ?=?
但是如果遇到列名也是需要用户传入的时候,就需要使用${key}
例如
sql select * from 表 where 列名是动态的${columnName} = 动态的值 #{columnValue}
总结:动态值 使用#{key} ; 动态列名 容器名 关键字 使用${key}
传值问题:
简单类型传入(单个值)
概念说明:
这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
- 简单类型:只包含一个值的数据类型
- 基本数据类型:int、byte、short、double、……
- 基本数据类型的包装类型:Integer、Character、Double、……
- 字符串类型:String
- 复杂类型:包含多个值的数据类型
- 实体类类型:Employee、Department、……
- 集合类型:List、Set、Map、……
- 数组类型:int[]、String[]、……
- 复合类型:List<Employee>、实体类中包含集合……
<!-- 场景1 传入单个简单类型 key 随便写 一般情况下推荐使用参数名 -->
<delete id="deleteById">
delete from t_emp where emp_id=#{ergouzi}
</delete>
<select id="queryBySalary" resultType="org.example.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_salary=#{salary}
</select>
单个实体对象传入
注意:做插入,删除,更新操作时需要提交事务
<!-- 场景2 传入的是一个实体对象 key 如何写?
key = 对象的属性名即可
-->
对应接口里的方法
//插入员工数据【传入实体对象】
int insertEmp(Employee employee);
<insert id="insertEmp">
insert into t_emp (emp_name,emp_salary) value (#{empName},#{empSalary});
</insert>
多个简单类型传入
<!-- 场景3 传入多个简单类型数据如何取值 key
key 不可以随意取名
key 也不可以按照形参名称取值
方案一:注解指定 @Param注解 指定多个简单参数的key-----------------推荐使用
在形参前面加上此 @Param("标识") 形参类型 形参名;注解中的标识,就代表该形参,写对应key时就写该形参
List<Employee> queryByNameAndSalary(@Param("name") String name,@Param("salary") Double salary);
方案二:mybatis默认机制
arg0 arg1 ... 形参从左到右依次对应arg0,arg1...
(name,salary) name->key=arg0 salary->key=arg1
param1 param2 ...
(name,salary)name-> key=param1 salary->key=param2
List<Employee> queryByNameAndSalary( String name, Double salary);
-->
<!--方案一:-->
<select id="queryByNameAndSalary" resultType="org.example.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp where emp_name=#{name} and emp_salary=#{salary}
</select>
<--方案二:-->
<select id="queryByNameAndSalary" resultType="org.example.pojo.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp where emp_name=#{arg0} and emp_salary=#{arg1}
</select>
返回值类型
返回单个简单类型
<!-- 返回单个简单类型如何指定
resultType的写法! 返回值的类型
resultType语法:
类的全限定符号
别名简称
mybatis给我们提供了72种默认的别名!
这些都是我们常用的Java数据类型! 【Java常用的数据类型】
基本数据类型 int double -> _int _double
包装数据类型 Integer Double -> int/Integer double
集合容器类型 Map List HashMap -> 小写即可 map list hashmap
如果没有提供的需要自己定义或者写类的全限定符
扩展 如果没有提供提供,需要自己定义或写类的全限定符
给自己声明的类如何定义别名!!
在typeAliases>标签中定义:(该标签在setting下,environment上——————mybatis-config.xml中)
单独定义
<typeAlias type="org.example.pojo.Employee" alias="employee"/>
批量定义(批量将包下的类给与别名,别名为类名首字母小写!)
<package name="org.example.pojo"/>
-->
<select id="queryNameById" resultType="String">
select emp_name empName from t_emp where emp_id=#{id}
</select>
<select id="querySalaryById" resultType="double">
select emp_salary from t_emp where emp_id=#{id}
</select>
返回单个自定义实体类型
<!--场景2 返回当单个自定义实体类型
resultType
方式一:取类的全限定符
方式二:取类的别名
默认要求:
查询,返回单个实体类型,要求列名和属性名要一致!
这样才可以进行实体类的属性映射
自动映射的设置方式:
在mybatis-config.xml的setting标签中设置:
开启驼峰式自动映射 数据库 a_column java aColumn
<setting name="mapUnderscoreToCamelCase" value="true"/>
-->
<select id="queryById" resultType="org.example.pojo.Employee">
select * from t_emp where emp_id=#{id}
</select>
返回map数据类型
对应方法:
//查询部门的最高工资和平均工资
Map<String,Object> selectEmpNameAndMaxSalary();
<!-- 场景3 返回map数据类型
对于放回的值没有实体类时可以使用map类型接收 (列名-》key;结果-》值)
-->
<select id="selectEmpNameAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>
返回List集合类型数据
<!-- 场景4 返回List集合类型
//查询工资高于传入值的员工的姓名
List<String> queryNameBySalary(Double salary);
//查询全部员工信息
List<Employee> queryAll();
注意:返回值是集合时 resultType不是填list,而是填集合的泛型
为什么:
mybatis -> ibatis -> selectOne 单个|selectList 多个 -》selectOne调用selectList
-->
<select id="queryNameBySalary" resultType="string">
select emp_name from t_emp where emp_salary>#{salary}
</select>
<select id="queryAll" resultType="org.example.pojo.Employee">
select * from t_emp
</select>
说明:返回结果时map类型时,需要查询结果映射,所以指定返回值类型为map,而list集合存储的只是对象,所以只需要指定泛型。
自动提交事务和自增长主键回显(返回主键值)
自动提交事务:在创建sqlSession时向openSession()方法中传入true
例如sqlSession=sqlSessionFactory.openSession(true)
手动提交事务
sqlSession.commit();
自增长主键回显
<!-- 场景5 主键回显 获取插入数据的主键
1 自增长主键回显 由mysql维护 auto_increment
//员工插入
int insertEmp(Employee employee);
想要主键回显 设置以下几个属性
useGeneratedKeys="true" 我们想要数据库自动增长的主键值
keyColumn="emp_id" 主键列的值(填入自增长主键列名)
keyProperty="empId" 接收主键列值的属性
-->
<insert id="insertEmp" useGeneratedKeys="true" keyColumn="emp_id" keyProperty="empId">
insert into t_emp (emp_name,emp_salary)
value(#{empName},#{empSalary})
</insert>
非自增长类型的维护
在teacharMapper.xml文件中
<!--
将非自增长主键交由mybatis维护
期望,非自增长主键,交给mybatis帮助我们维护!
<selectKey-------------------------插入语
order="BEFORE|AFTER" sql语句是在插入语句之前还是之后执行
resultType="string" 插入语句返回值类型
keyProperty="tId" 查询结果给那个属性赋值
-->
<insert id="insertTeacher">
<selectKey order="BEFORE" resultType="string" keyProperty="tId">
select replace(UUID(),'-','');
</selectKey>
insert into teacher (t_id,t_name)
value(#{tId},#{tName})
</insert>
多表映射
多表映射实体类设计
多表映射需要做的事
多表查询:
1多表查询的sql语句需要我们编写
2 自己设计存储数据的实体类(承接多个表查询的结果)
3自己定义结果集映射(resultMap)
多表实体类存储设计
实体类设计关系(查询):
实体类设计:对一关系下,类中只要包含单个对方对象类型属性即可!
实体类设计:对多关系下,类中只要包含对方类型集合属性即可!
深层映射
使用<resultMap标签、
声明resultMap标签,自定义映射规则
id标识-》select resultMap=“标识”
type ->具体的返回值类型 全限定符和别名|集合只写泛型即可
《id 主键映射关系
《result 普通列映射关系
|关联关系 | 配置项关键词|所在配置文件和具体位置| |
|对一 | association标签/javaType属性/property属性|Mapper配置文件中的resultMap标签内| |
|对多 | collection标签/ofType属性/property属性|Mapper配置文件中的resultMap标签内| |
格式:
<resultMap id="代表此resultMap的标识" type="返回值类型">
<!--
第一层属性order对象
id——————order的主键
result——————order的普通列(customer_id在Customer表中是主键列,但在Order表中只是普通列)
-->
<id column="列名" property="对应属性名"/>
<result column="列名" property="对应属性名"/>---------------如果开启了驼峰式映射reslut标签可省略
<!--
(对一)第二层对象属性赋值——————使用association标签
property 对象属性名
javaType 对象类型
在association双标签中,<id <result
-->
<association property="对象名" javaType="对象类型">
<id column="列名" property="对应属性名"/>
<result column="列名" property="对应属性名"/>
注意:要开启深层映射
多表映射的优化-------------------------------开启深层映射
默认情况下,resultMap会自动映射单层的result标签[前提:列名或属性名相同,或者开启了驼峰式映射后相同]
嵌套association|collection不会自动映射result标签
如何解决:开启深层次的自动映射
在mybatis-config.xml文件的setting标签中
<setting name="autoMappingBehavior" value="FULL"/>
开启深层自动映射 不管resultMap有没有嵌套都会自动映射result标签的属性和列
</association>
(对多) 给集合属性赋值——————使用collection标签
property 集合属性名
ofType 集合的泛型类型
在collection双标签中 <id <result
<collection property="集合名" ofType="集合泛型">
<id column="列名" property="对应属性名"/>
<result column="列名" property="对应属性名"/>
</collection>
</resultMap>
代码举例
对一代码:
@Data
public class Order {
private Integer orderId;
private String orderName;
private Integer customerId;
//一个订单对应一个客户 对一
//对象装对应的客户信息
private Customer customer;
}
public interface OrderMapper {
//根据id查询订单信息和订单对应的客户
Order queryOrderById(Integer id);
}
在OrderMapper.xml中
<resultMap id="orderMap" type="order">
<!--
第一层属性order对象
id——————order的主键
result——————order的普通列(customer_id在Customer表中是主键列,但在Order表中只是普通列)
-->
<id column="order_id" property="orderId"/>
<result column="order-name" property="orderName"/>
<result column="customer_id" property="customerId"/>
<!--
第二层对象属性赋值——————使用association标签
property 对象属性名
javaType 对象类型
在association双标签中,<id <result
-->
<association property="customer" javaType="customer">
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
</association>
</resultMap>
<select id="queryOrderById" resultMap="orderMap">
SELECT * FROM t_order tor JOIN t_customer tur
ON tor.`customer_id`=tur.`customer_id`
WHERE tor.`order_id`=#{id}
</select>
一对多代码
@Data
public class Customer {
private Integer customerId;
private String customerName;
//一个客户对应多个订单
//对多:装对方类型的集合即可
private List<Order> orderList;
}
public interface CustomerMapper {
//查询所有的客户信息以及客户对应的订单信息
List<Customer> queryList();
}
在CustomerMapper.xml中
<!--
给集合属性赋值——————使用collection标签
property 集合属性名
ofType 集合的泛型类型
在collection双标签中 <id <result
-->
<resultMap id="customerMap" type="customer">
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
<collection property="orderList" ofType="order">
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
<result column="customer_id" property="customerId"/>
</collection>
</resultMap>
<select id="queryList" resultMap="customerMap">
SELECT * FROM t_order tor JOIN t_customer tur
ON tur.`customer_id`=tor.`customer_id`
</select>