MyBatis
一、MyBatis:是一款持久层的框架 ;简化crud 的
二、MyBatis 的快速入门 演示
2.1、准备MyBatis 需要的依赖
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
2.2、定义一个实体类 来存放查询返回的结果
public class Employee {
private Integer empId;
private String empName;
private Double empSalary;
//getter | setter
}
2.3、生成一个Mapper接口
package com.zh.Mapper;
import com.zh.Pojo.Employee;
/**
* @author ZhouHao
* @version 1.0
* 拼不了爹,只能拼命
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 2023/10/6 17:43
*/
public interface emp_Mapper {
// 根据id 查询员工的信息
Employee select_id(int id);
Employee delete_id(int id);
}
2.4、生成一个 Mapper.xml文件(专门来写sql语句的) 跟上面接口形成映射关系
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">
<!--
一、一个标签对应着接口的一个方法 这就是方法的实现
id 对应接口方法名 result 对应接口方法的返回值(返回值是全限定名)
二、Mapper 接口不能重载(重名不同参数) ;因为xml文件根据方法名称识别的
-->
<select id="select_id" resultType="com.zh.Pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_id = #{id}
</select>
<delete id="delete_id" >
delete from t_emp where emp_id= #{id}
</delete>
</mapper>
2.5、准备MyBatis的配置文件(配置数据库的连接信息和性能配置);习惯上命名为 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>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="Mappers/emp_Mapper.xml"/>
</mappers>
</configuration>
2.6、运行测试
@Test
public void test_01() throws IOException {
// 1、读取外部配置文件(Mybatis_Config.xml)(连接数据库的)
InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
// 2、创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
// 3、获取sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、获取接口的代理对象 (代理技术)调用代理对象的方法,就会查找mapper接口的方法
emp_Mapper mapper = sqlSession.getMapper(emp_Mapper.class);
Employee employee = mapper.select_id(1);
System.out.println("employee = " + employee);
// 5、提交事务(非DQL(查询))和释放资源
sqlSession.commit();
sqlSession.close();
}
MyBatis 的基本使用
一 、取值符合 #{} 和 ${} 号的区别
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">
<!-- #{key} :占位符 + 赋值 emp_id =? ?=赋值 推荐使用 防止【注入攻击】的问题
${key} :字符串拼接 emp_id = +id
#{} 只能代替值的位置 不能代替 列名 容器名 sql关键字 等 只能 emp_id =?
${} 可以代替上面这些 列名是动态的 ${列名} =动态值#{值} 可以 ? = ?
-->
<select id="select_id_01" resultType="com.zh.Pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_id = #{id}
</select>
<select id="select_id_02" resultType="com.zh.Pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where ${arg0} = #{arg1}
</select>
</mapper>
数据的输入
接口:
package com.zh.Mapper;
import com.zh.Pojo.Employee;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @author ZhouHao
* @version 1.0
* 拼不了爹,只能拼命
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 2023/10/7 19:03
*/
public interface emp_Mapper {
Employee select_id_01(int id);
// 插入员工数据[传入实体对象]
int insert_03(Employee employee);
// 根据 名字 和 工资 去查询员工信息
List<Employee> query_04(@Param("a") String name ,@Param("b") Double salary);
Employee select_id_02(String emp_id ,int id);
// map
int insert_05(Map map);
}
Mapper.xml :
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">
<!-- #{key} :占位符 + 赋值 emp_id =? ?=赋值 推荐使用 防止【注入攻击】的问题
${key} :字符串拼接 emp_id = +id
#{} 只能代替值的位置 不能代替 列名 容器名 sql关键字 等 只能 emp_id =?
${} 可以代替上面这些 列名是动态的 ${列名} =动态值#{值} 可以 ? = ?
-->
<select id="select_id_01" resultType="com.zh.Pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_id = #{id}
</select>
<!-- 二、传入一个实体类对象 key应该怎么写 key=这个类的属性名 -->
<insert id="insert_03">
insert into t_emp (emp_name,emp_salary) values (#{empName},#{empSalary})
</insert>
<!-- 三、传入多个参数的写法-->
<!--方式一、 使用注解的方式指定-->
<select id="query_04" resultType="com.zh.Pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_name = #{a} and emp_salary =#{b}
</select>
<!-- 方式二、使用 age0 age1 的方式指定 等价于 param1 param2-->
<select id="select_id_02" resultType="com.zh.Pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where ${arg0} = #{arg1}
</select>
<!-- Map 应该怎么写 key=等于map 的key -->
<insert id="insert_05">
insert into t_emp (emp_name,emp_salary) values (#{name},#{salary})
</insert>
</mapper>
Test:
@Test
public void test_01() throws IOException {
InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
SqlSession sqlSession = sqlSessionFactory.openSession();
emp_Mapper mapper = sqlSession.getMapper(emp_Mapper.class);
Employee employee = mapper.select_id_01(1);
// 传入一个实体对象应该怎么写
Employee employee1 = new Employee();
employee1.setEmpName("周浩");
employee1.setEmpSalary(100.0);
mapper.insert_03(employee1);
// 多个参数传入的实现
// 方式一、 使用注解的方式实现
List<Employee> query=mapper.query_04("周浩",100.0);
// 方式二、 使用age0 age1 的方式实现
Employee employee2 = mapper.select_id_02("emp_id",1);
// 演示 may的写法
HashMap<Object, Object> map = new HashMap<>();
map.put("name","zhou");
map.put("salary",102.0);
mapper.insert_05(map);
System.out.println("employee = " + employee);
System.out.println("employee2 = " + employee2);
System.out.println(query);
sqlSession.commit();
sqlSession.close();
}
数据的输出
1、resultType 的值应该怎么写 别名应该怎么取
2、开启驼峰命名自动映射
3、使用map接值当value是多个的时候会报错
4、List接值
5、主键回显 (自增长的主键)
6、自己定义映射关系 resultMap
接口:
package com.zh.Mapper;
import com.zh.Pojo.Employee;
import java.util.List;
import java.util.Map;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/8 15:04
*/
public interface emp_Mapper {
// 根据id 来查询 员工的姓名
String select_id_01(int id);
// 根据id 来查询 员工的薪水
double select_id_02(int id);
// 根据 id 返回员工的所有信息
Employee select_id_03(Integer id);
// 根据 name 返回员工信息
List<Employee> select_name_04(String name );
// 查询平均工资,使用map接值
Map<String,Object> selectEmpNameAndMaxSalary();
// 查询员工工资高于200 的员工姓名
List<String> select_salary_05(int salary);
// 查询所有员工的信息
List<Employee> select_06();
// 插入一条数据
int insert_07(Employee employee);
// 根据id 查询信息
Employee select_08( int id );
}
mapper.xml 的配置文件应该怎么写
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">
<!-- 一、resultType 的值应该怎么写 两种写法
1、全部限定类名 java.lang.String
2、别名简写 double mabatis提供了 72种别名的书写方法
扩展:自己定义的类 怎么定义别名 在 MyBatis_Config.xml 的<typeAliases>标签下配置
-->
<select id="select_id_01" resultType="java.lang.String">
select emp_name empName from t_emp where emp_id = #{id}
</select>
<select id="select_id_02" resultType="double">
select emp_salary from t_emp where emp_id = #{id}
</select>
<!-- 别名的定义演示-->
<select id="select_id_03" resultType="zhou">
select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id=#{id}
</select>
<!-- 二、因为要求 列名和 属性名 要保持一致 这样才可以进行实体类的映射-->
<!-- 可以在MyBatis_Config.xml 文件中 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<!-- 这样就可以不用给列名去取别名了-->
<select id="select_name_04" resultType="zhou">
select * from t_emp where emp_name=#{name};
</select>
<!-- 三、使用map接值 key=查询列名 value=查询的值 当返回的值是多个的话 会报错哦!-->
<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 集合类型 resultType=集合的泛型就好了 底层还是调用了selectList的-->
<select id="select_salary_05" resultType="String">
select emp_name from t_emp where emp_salary > #{salary}
</select>
<select id="select_06" resultType="zhou">
select * from t_emp
</select>
<!-- 五、主键回显 获取插入主键的数据
useGeneratedKeys="true" 我们想要数据库自动增长的主键值
keyColumn="emp_id" 告诉mybatis 主键的列
keyProperty="empId" 接收主键值的属性列
-->
<insert id="insert_07" useGeneratedKeys="true" keyColumn="emp_id" keyProperty="empId">
insert into t_emp (emp_name,emp_salary) values (#{empName},#{empSalary})
</insert>
<!-- 六 、自定义映射关系
6.1、当 列名和属性名不一致的时候的解决方法 1、自定义别名 2、开启驼峰命名自动映射 3、自定义映射关系ResuleMap
6.2 resultType 和 resultMap的区别
resultType:只能映射一层结构,深层次的对象结构无法映射 就是多表查询的结构无法映射
resultMap : 自己定义映射关系
id :自己定义的标识
type:具体的返回类型 全限定类名 |别名 |集合list写泛型
<id/> 主健的映射关系
<result> 普通的列的映射关系
-->
<!-- 自己定义 别名 主要是可以开启多成映射-->
<resultMap id="aa" type="zhou">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"></result>
<result column="emp_salary" property="empSalary"></result>
</resultMap>
<select id="select_08" resultMap="aa">
select * from t_emp where emp_id=#{id};
</select>
</mapper>
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"/>
<!-- 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 定义别名的方式 一个一个类定义别名-->
<typeAlias type="com.zh.Pojo.Employee" alias="zhou"/>
<!-- 整个包下定义别名 它的别名就是类的首字母小写 不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
<package name="com.zh"/>
</typeAliases>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 JDBC | MANAGED JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
<transactionManager type="JDBC"/>
<!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="Mappers/emp_Mapper.xml"/>
</mappers>
</configuration>
Test:
@Test
public void test_01() throws IOException {
// 1、读取外部配置文件(Mybatis_Config.xml)(连接数据库的)
InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
// 2、创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
// 3、获取sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4、获取接口的代理对象 (代理技术)调用代理对象的方法,就会查找mapper接口的方法
// kdl动态代理对象生成mapper代理对象
emp_Mapper mapper = sqlSession.getMapper(emp_Mapper.class);
// 内部拼接全限定符 + 方法名 去查找sql语句标签
// mybatis 底层依然调用 ibatis 只不过 有固定的模式供我们使用
String aa=mapper.select_id_01(1);
System.out.println(aa);
Employee employee=mapper.select_id_03(1);
System.out.println("employee~~~ = " + employee);
List<Employee> bb=mapper.select_name_04("周浩");
System.out.println("bb = " + bb);
// 当返回的值是多个的话 会报错哦!
// Map<String ,Object> map_01= mapper.selectEmpNameAndMaxSalary();
// System.out.println("map_01 = " + map_01);
List<String> list_01=mapper.select_salary_05(200);
List<Employee> list_02=mapper.select_06();
System.out.println("list_02 = " + list_02);
System.out.println("list_01 = " + list_01);
Employee employee1 = new Employee();
System.out.println("没插入前的主键"+ employee1.getEmpId());
employee1.setEmpName("周浩啊");
employee1.setEmpSalary(8888.0);
int rows =mapper.insert_07(employee1);
System.out.println("rows = " + rows);
System.out.println("插入数据后的主键" + employee1.getEmpId());
Employee employee2=mapper.select_08(1);
System.out.println("employee2 = " + employee2);
// 5、提交事务(非DQL(查询))和释放资源
sqlSession.commit();
sqlSession.close();
}
6、非自动增长的主键怎么交给MyBatis维护 p82
crud练习
@beforeEach:在Test执行先 ;先执行beforeEach这个方法
@AfterEach:junit会在每一个@Test方法最后执行@BeforeEach方法
接口:
package com.zh.UserMapper;
import com.zh.Pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/9 21:54
*/
public interface User_Mapper {
int insert(User user);
int update(User user, int id);
int delete(Integer id);
User selectById(Integer id);
List<User> selectAll();
}
实现类:
package com.zh.Pojo;
import lombok.Data;
@Data //自动生成get set方法 不过要下一个插件 lombok
public class User {
private Integer id;
private String username;
private String password;
}
mapper.xml
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.UserMapper.User_Mapper">
<!-- 主键回显-->
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user (username,password) values (#{username},#{password})
</insert>
<update id="update" >
update user set username=#{username},password=#{password} where id=#{id}
</update>
<delete id="delete" >
delete from user where id=#{id}
</delete>
<select id="selectById" resultType="user">
select * from user where id=#{id}
</select>
<select id="selectAll" resultType="user">
select * from user
</select>
</mapper>
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"/>
<!-- 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 定义别名的方式 一个一个类定义别名-->
<!-- <typeAlias type="com.zh.Pojo.Employee" alias="zho"/>-->
<!-- 整个包下定义别名 它的别名就是类的首字母小写 不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
<package name="com.zh"/>
</typeAliases>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 JDBC | MANAGED JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
<transactionManager type="JDBC"/>
<!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="Mappers/Mapper.xml"/>
</mappers>
</configuration>
Test:
package com.zh;
import com.zh.Pojo.User;
import com.zh.UserMapper.User_Mapper;
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 org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/9 22:15
*/
public class Test_user {
public SqlSession sqlSession;
public User_Mapper mapper;
public User user;
@BeforeEach //junit会在每一个@Test方法前执行@BeforeEach方法
public void before() throws IOException {
InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
sqlSession = sqlSessionFactory.openSession();
mapper = sqlSession.getMapper(User_Mapper.class);
user = new User();
user.setId(8);
user.setUsername("zzzz");
user.setPassword("123");
}
@AfterEach //junit会在每一个@Test方法最后执行@BeforeEach方法
public void after(){
sqlSession.commit(); //执行dml语句的时候 必须要提交事务 ,否则的话就不会插入数据
sqlSession.close();
}
@Test
public void insert_01(){
mapper.insert(user);
System.out.println(user.getId()); //主键回显
}
@Test
public void update_02(){
mapper.insert(user);
}
@Test
public void delete_03(){
mapper.delete(10);
}
@Test
public void select_id_04(){
User user1 = mapper.selectById(11);
System.out.println("user1 = " + user1);
}
@Test
public void select_all_04(){
List<User> users = mapper.selectAll();
System.out.println("users = " + users);
}
}
MyBatis的多表映射
多表查询应该怎么做?
1、多表查询的sql语句还是得我们自己写(连接查询)
2、自己设计存储数据的实体类(承受多个表的结果)
小技巧 : 1对1 的话:实体类属性中包含对方的对象
1对多的话:实体类属性中包含对方的对象集合
3、自己定义结果集映射(resultMap)
1对1的话:association标签写第二层属性结构
1对多的话在collection标签中写list结构
快速入门:
接口:
1对1
package com.zh.Mapper;
import com.zh.Pojo.Order;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 11:33
*/
//根据id 的订单号来查询 客户表的信息
public interface Id_Select_Customer {
Order query_OrderId(int id);
}
1对多:
package com.zh.Mapper;
import com.zh.Pojo.Order;
import java.util.List;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 15:24
*/
public interface name_select_Order {
// 根据客户的名字来查询 有多少个订单
List<Order> select_00(String name);
}
实现类:
customer:
package com.zh.Pojo;
import lombok.Data;
import java.util.List;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 11:27
*/
@Data
public class Customer {
private int customerId;
private String customerName;
// 根据客户的名字来查询 客户有多少个订单
// 是一对多的关系 所有要写一个订单的集合
private List<Order> orderList;
}
order:
package com.zh.Pojo;
import lombok.Data;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 11:27
*/
@Data
public class Order {
private int orderId;
private String orderName;
private int customerId;
// 一个订单对应着一个客户 所以还需要一个客户对象
private Customer customer;
}
mapper.xml
1对1:
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.Id_Select_Customer">
<resultMap id="aaa" type="order">
<id column="order_id" property="orderId" />
<result column="order_name" property="orderName"/>
<result column="customer_id" property="customerId"/>
<!--association 对象属性赋值 就是另一个表 第二层结构
javaType 另一个表的全限定类(这里取了别名)
-->
<association property="customer" javaType="customer">
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
</association>
</resultMap>
<select id="query_OrderId" resultMap="aaa">
select * from t_order tor
join t_customer tc on tor.customer_id = tc.customer_id
where order_id=#{id};
</select>
</mapper>
1对多:
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.name_select_Order">
<resultMap id="aa" type="customer">
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
<!-- collection 用来存放集合的 ofType 是用来存放集合的泛型的 -->
<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="select_00" resultMap="aa">
select * from
t_customer tc
join
t_order t
on tc.customer_id = t.customer_id
where tc.customer_name=#{name}
</select>
</mapper>
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"/>
<!-- 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 定义别名的方式 一个一个类定义别名-->
<!-- <typeAlias type="com.zh.Pojo.Employee" alias="zho"/>-->
<!-- 整个包下定义别名 它的别名就是类的首字母小写 不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
<package name="com.zh"/>
</typeAliases>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 JDBC | MANAGED JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
<transactionManager type="JDBC"/>
<!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="Mappers/name_select_mapper.xml"/>
</mappers>
</configuration>
映射优化:
在MyBatis_Config.xml文件中开启
在mapper.xml文件中 result标签中的属性就会帮我们自动的映射了
Mybatis的动态语句
一、if where标签的使用
二、set标签的使用
三、trim 标签的使用
四、choose、when、otherwise 标签的使用
五、forech 标签的使用
六、sql片段的调用
下面是上面知识点的总结
接口:
package com.zh.Mapper_interface;
import com.zh.Pojo.Employee;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 19:41
*/
public interface emp_mapper {
// 根据员工的 姓名 和 工资 查询 员工的信息
List<Employee> query_name_salary(@Param("aa") String name,@Param("bb") double salary);
// 演示标签 choose when otherwise 标签的使用
List<Employee>query_name_salary_02(@Param("zz") String name,@Param("hh") double salary);
// 根据员工id更新员工的数据,我们要求传入的name 和 salary 不为null 才更新
int update_name_salary(@Param("employee")Employee emp,@Param("zhou") int _id);
// 演示 Trim标签的使用
List<Employee> queryTrim(@Param("aa") String name,@Param("bb") double salary);
// 演示 foreach 怎么使用
// 根据id 批量查询
List<Employee> queryBatch(@Param("ids") List<Integer> ids);
// 根据id 的批量删除
int deleteBatch(List<Integer> id);
int insertBatch(List<Employee> employeeList);
int updateBatch(List<Employee> employeeList);
}
实现类:
package com.zh.Pojo;
import lombok.Data;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 19:36
*/
@Data //会生成get set方法
public class Employee {
private int empId;
private String empName;
private double empSalary;
}
mapper.xml:
<?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">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper_interface.emp_mapper">
<!--
<if/> :判断传入的参数是否加入语句
test属性:内部作比较 true则拼接sql语句 false不拼接
大于小于 推荐使用 实体符号 >== > <== <
<where> : 当where中有一个if满足的时候 自动的会添加where关键字
当if标签都不满足的话 会去掉 后面的关键字
-->
<select id="query_name_salary" resultType="employee">
<!-- 调用代码最下面的 sql片段 include表示调用下面的-->
<include refid="zhouhao"></include>
<where>
<if test="aa != null">
emp_name=#{aa}
</if>
<if test="bb > 50 and bb < 200">
and emp_salary=#{bb}
</if>
</where>
</select>
<!--set 1、自动去掉多余的 ,逗号
2、自动添加set关键字
注意: 下面二个if标签必须要满足一个 否则的话语法会报错 因为两个都不满足的话 语法本身就是错的
-->
<update id="update_name_salary">
update t_emp
<set>
<if test="employee.empName!=null">
emp_name=#{employee.empName},
</if>
<if test="employee.empSalary!=null">
emp_salary=#{employee.empSalary}
</if>
</set>
where emp_id=#{zhou}
</update>
<!-- trim 标签可以在 控制条件部分 的两端多余的字符 有4个属性
prefix: 动态的添加前缀
suffix: 动态的添加后缀
prefixOverrides: 指定要动态的去除前缀那些关键字 有多个值的话使用 | 分隔
suffixOverrides: 指定要动态的去除后缀那些关键字 有多个值的话使用 | 分隔
不止可以用在 select 也可以用在的sql语句上
-->
<select id="queryTrim" resultType="employee">
select * from t_emp
<!-- prefix 下面语句有一个满足 就添加 where关键字 -->
<!-- prefixOverrides 如果第一个条件false 就去除前缀的关键字and 或 or 的-->
<trim prefix="where" prefixOverrides="and | or">
<if test="bb > 50 and bb < 200">
emp_salary=#{bb}
</if>
<if test="aa != null">
and emp_name=#{aa}
</if>
</trim>
</select>
<!-- 演示 choose when otherwise 标签
-->
<select id="query_name_salary_02" resultType="employee">
select * from t_emp where
<!-- choose 里面只会有一个条件生效 第一个when生效了就不会执行下面的了
最少要满足一个生效
-->
<choose>
<when test="zz!=null">
emp_name=#{zz}
</when>
<when test="hh!=null">
emp_salary=#{hh}
</when>
<otherwise>1=1</otherwise>
</choose>
</select>
<!-- 演示:foreach 的标签的使用:常用的4个属性
collection:要遍历的值 ids | arg0 | list
open:遍历之前要追加的字符串
close:遍历结束要添加的字符串
separator: 每次遍历的分隔符 ,如果是最后一次的话 不会追加
item: 获取每个遍历项
-->
<select id="queryBatch" resultType="employee" >
select * from t_emp where emp_id in
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
<delete id="deleteBatch">
delete from t_emp where emp_id in
<foreach collection="list" open="(" close=")" separator="," item="bb">
#{bb}
</foreach>
</delete>
<insert id="insertBatch">
insert into t_emp (emp_name,emp_salary) values
<foreach collection="list" separator="," item="emp">
(#{emp.empName},#{emp.empSalary})
</foreach>
</insert>
<!-- 因为 update 批量更新不了 所以就给他整体遍历-->
<update id="updateBatch">
<!-- zh 相当于是 list里面的一个一个对象 下面这条语句就相当于是 几条语句在执行-->
<foreach collection="list" item="zh">
update t_emp set emp_name=#{zh.empName},emp_salary=#{zh.empSalary} where emp_id=#{zh.empId};
</foreach>
</update>
<!--
演示sql片段提取 提取上面 query_name_salary 的select * from t_emp语句
在 这个上面 那个标签中演示了怎么使用
-->
<sql id="zhouhao">
select * from t_emp
</sql>
</mapper>
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"/>
<!-- 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启了map的自动映射结果集了 除了主键还是要自己写的 result就不用我们自己写了-->
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
<typeAliases>
<!-- 定义别名的方式 一个一个类定义别名-->
<!-- <typeAlias type="com.zh.Pojo.Employee" alias="zho"/>-->
<!-- 整个包下定义别名 它的别名就是类的首字母小写 不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
<package name="com.zh"/>
</typeAliases>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 JDBC | MANAGED JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
<transactionManager type="JDBC"/>
<!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- ?allowMultiQueries=true 表示一次性开启多条sql语句到数据库中执行-->
<property name="url" value="jdbc:mysql:///mybatis-example?allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="Mappers/emp_mapper.xml"/>
</mappers>
</configuration>
Test:
package com.zh.Test;
import com.zh.Mapper_interface.emp_mapper;
import com.zh.Pojo.Employee;
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 org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @author ZhouHao
* @version 1.0
* 只要干不死,就往死里干
* 成为想成为的人,越努力越幸运
* 我自光芒万丈,何须他人半点光
* 有要做的事,请从知晓的那一刻百分之百去执行
* 2023/10/11 21:38
*/
public class Dome {
public SqlSession sqlSession;
public emp_mapper mapper;
@BeforeEach //junit会在每一个@Test方法前执行@BeforeEach方法
public void before() throws IOException {
InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
sqlSession = sqlSessionFactory.openSession();
mapper = sqlSession.getMapper(emp_mapper.class);
}
@AfterEach //junit会在每一个@Test方法最后执行@BeforeEach方法
public void after(){
sqlSession.commit(); //执行dml语句的时候 必须要提交事务 ,否则的话就不会插入数据
sqlSession.close();
}
// 演示 where if 标签的使用
@Test
public void test_01(){
List<Employee> list = mapper.query_name_salary("周浩", 150);
System.out.println("list = " + list);
Employee employee = new Employee();
// employee.setEmpId(9);
employee.setEmpName("周浩");
// employee.setEmpSalary(100.0); //让 salary为空(就是不设置他的值)
int i = mapper.update_name_salary(employee,9);
}
// 演示 trim 标签的使用
@Test
public void test_02(){
List<Employee> list = mapper.queryTrim("周浩", 10);
System.out.println("list = " + list);
}
// foreach 查询 标签的使用
@Test
public void test_03(){
ArrayList<Integer> id = new ArrayList<>();
id.add(1);
id.add(2);
id.add(3);
List<Employee> list = mapper.queryBatch(id);
System.out.println("list = " + list);
}
// foreach 批量删除
@Test
public void test_04(){
ArrayList<Integer> id = new ArrayList<>();
id.add(1);
id.add(2);
id.add(3);
int i = mapper.deleteBatch(id);
System.out.println("i = " + i);
}
// foreach 批量插入
@Test
public void test_05() {
List<Employee> employees = new ArrayList<>();
Employee employee = new Employee();
employee.setEmpName("4545");
employee.setEmpSalary(150);
employees.add(employee);
Employee employee1 = new Employee();
employee1.setEmpName("zhouzh");
employee1.setEmpSalary(125);
employees.add(employee1);
int i = mapper.insertBatch(employees);
System.out.println("i = " + i);
}
// foreach 批量 更新的操作
@Test
public void text_07(){
Employee employee = new Employee();
employee.setEmpId(1);
employee.setEmpName("张健健");
employee.setEmpSalary(180);
List<Employee> list = new ArrayList<>();
list.add(employee);
int i = mapper.updateBatch(list);
System.out.println("i = " + i);
}
// 演示 choose when otherwise
@Test
public void test_06(){
List<Employee> list = mapper.query_name_salary_02(null, 150);
System.out.println("list = " + list);
}
}
Mapper高级扩展
一、批量打包操作 p96
二、PageHelper 分页插件的使用
注意:sql语句后面不要加 ; 号
会拦截sql语句帮我们追加 limit 语句
2.1、导入依赖
<!-- 分页插件需要的依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
2.2、在MyBatis_Config.xml 中配置分页
3.3、分页插件的使用
@Test
public void test_01(){
// 调用之前要设置分页数据 当前第几页 每页显示多少个
PageHelper.startPage(1,2);
List<Employee> employees = mapper.query_all();
// 将查询的数据放到一个PageInfo 的实体类中
PageInfo<Employee> aa =new PageInfo<>(employees);
// 当前页的数据 获取的条数 获取的总页数
int pages = aa.getPages();
System.out.println("pages = " + pages); //数据
long total = aa.getTotal();
System.out.println("total = " + total); //条数
int pageNum = aa.getPageNum();
System.out.println("pageNum = " + pageNum);//页数
}
}
三、orm介绍和逆向工程
1、orm是什么:应用在持久层上,使用面对对象思维进行数据库(面向过程)操作
hibernate:全自动的orm框架;提供了crud 且自动生成sql语句
MyBatis:半自动的orm框架 ;提供了crud 要自己写sql语句
2、半自动的orm框架,希望也能实现单表的crud自动生成 这个过程叫逆向工程
逆向工程:半自动orm 向 全自动orm 迈进
3、myBatis使用插件实现单表的sql语句自动生成
3.1、插件:
没啥用,不学了 知道自个插件就好了