⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记链接👉https://github.com/A-BigTree/Code_Learning
⭐⭐⭐⭐⭐⭐
如果可以,麻烦各位看官顺手点个star~😊
如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆
文章目录
3 Mybatis基本用法
3.1 HelloWorld
3.1.1 物理建模
CREATEDATABASE`mybatis-example`;USE`mybatis-example`;CREATETABLE`t_emp`(
emp_id INTAUTO_INCREMENT,
emp_name CHAR(100),
emp_salary DOUBLE(10,5),PRIMARYKEY(emp_id));INSERTINTO`t_emp`(emp_name,emp_salary)VALUES("tom",200.33);INSERTINTO`t_emp`(emp_name,emp_salary)VALUES("jerry",666.66);INSERTINTO`t_emp`(emp_name,emp_salary)VALUES("andy",777.77);
3.1.2 逻辑建模
创建Maven项目加入依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version><scope>compile</scope></dependency>
创建Java实体类
实体类是和现实世界中某一个具体或抽象的概念对应,是软件开发过程中,为了管理现实世界中的数据而设计的模型。 实体类的多个不同的叫法:
- domain:领域模型;
- entity:实体;
POJO
:Plain Old Java Object;- Java bean:一个Java类;
/**
* 和数据库表 t_emp 对应的实体类
* emp_id INT AUTO_INCREMENT
* emp_name CHAR(100)
* emp_salary DOUBLE(10,5)
*
* Java 的实体类中,属性的类型不要使用基本数据类型,要使用包装类型。因为包装类型可以赋值为null,表示空,而基本数据类型不可以。
*/@Data@NoArgsConstructor@AllArgsConstructorpublicclassEmployee{privateInteger empId;privateString empName;privateDouble empSalary;}
3.1.3 搭建框架环境
导入依赖
<!-- Mybatis核心 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency><!-- junit测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.3</version></dependency>
配置文件
Mybatis全局配置文件:
习惯上命名为
mybatis-config.xml
,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEconfigurationPUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 --><environmentsdefault="development"><!-- environment表示配置Mybatis的一个具体的环境 --><environmentid="development"><!-- Mybatis的内置的事务管理器 --><transactionManagertype="JDBC"/><!-- 配置数据源 --><dataSourcetype="POOLED"><!-- 建立数据库连接的具体信息 --><propertyname="driver"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/mybatis-example"/><propertyname="username"value="root"/><propertyname="password"value="atguigu"/></dataSource></environment></environments><mappers><!-- Mapper注册:指定Mybatis映射文件的具体位置 --><!-- mapper标签:配置一个具体的Mapper映射文件 --><!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 --><!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 --><mapperresource="mappers/EmployeeMapper.xml"/></mappers></configuration>
- 注意:配置文件存放的位置是
src/main/resources
目录下
Mybatis 映射配置文件:
相关概念:ORM(Object Relationship Mapping)对象关系映射。
- 对象:Java的实体类对象;
- 关系:关系型数据库;
- 映射:二者之间的对应关系;
下表列举的是最简单的单表映射(一个表和一个类):
Java概念数据库概念类表属性字段/列对象记录/行
**注意:
EmployeeMapper.xml
所在的目录要和
mybatis-config.xml
中使用
mapper
标签配置的一致。**
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- mapper是根标签,namespace属性:在Mybatis全局范围内找到一个具体的Mapper配置 --><!-- 引入接口后,为了方便通过接口全类名来找到Mapper配置文件,所以通常将namespace属性设置为接口全类名 --><mappernamespace="com.atguigu.mybatis.dao.EmployeeMapper"><!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --><!-- resultType属性:指定封装查询结果的Java实体类的全类名 --><selectid="selectEmployee"resultType="com.atguigu.mybatis.entity.Employee"><!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符,在#{}内部还是要声明一个见名知意的名称 -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select></mapper>
3.1.4 测试代码
@TestpublicvoidtestSelectEmployee()throwsIOException{// 1.创建SqlSessionFactory对象// ①声明Mybatis全局配置文件的路径String mybatisConfigFilePath ="mybatis-config.xml";// ②以输入流的形式加载Mybatis配置文件InputStream inputStream =Resources.getResourceAsStream(mybatisConfigFilePath);// ③基于读取Mybatis配置文件的输入流创建SqlSessionFactory对象SqlSessionFactory sessionFactory =newSqlSessionFactoryBuilder().build(inputStream);// 2.使用SqlSessionFactory对象开启一个会话SqlSession session = sessionFactory.openSession();// 3.根据Mapper配置文件的名称空间+SQL语句的id找到具体的SQL语句// 格式是:名称空间.SQL语句的idString statement ="com.atguigu.mybatis.dao.EmployeeMapper.selectEmployee";// 要传入SQL语句的参数Integer empId =1;// 执行SQL语句Object result = session.selectOne(statement, empId);System.out.println("o = "+ result);// 4.关闭SqlSession
session.close();}
说明:
SqlSession
:代表Java程序和数据库之间的会话。(HttpSession
是Java程序和浏览器之间的会话);SqlSessionFactory
:是“生产”SqlSession
的“工厂”;- 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象;
3.2 HelloWorld强化
3.2.1 加入日志
在Mybatis工作过程中,通过打印日志的方式,将要执行的SQL语句打印出来。
加入依赖
<!-- 日志 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency>
加入配置文件
<?xml version="1.0" encoding="UTF-8"?><configurationdebug="true"><!-- 指定日志输出的位置,ConsoleAppender表示输出到控制台 --><appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 日志输出的格式 --><!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 --><pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern><charset>UTF-8</charset></encoder></appender><!-- 设置全局日志级别。日志级别按顺序分别是:TRACE、DEBUG、INFO、WARN、ERROR --><!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 --><rootlevel="INFO"><!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender --><appender-refref="STDOUT"/></root><!-- 根据特殊需求指定局部日志级别 --><loggername="com.atguigu.mybatis"level="DEBUG"/></configuration>
修改测试代码
这里我们仅仅修改查询结果的打印方式,为此需要做两件事:
- 在类上加
@Slf4j
注解; - 使用
Logger
对象打印数据;
if(result !=null){
log.info(result.toString());}
3.2.2 关联外部属性文件
在实际开发时,同一套代码往往会对应多个不同的具体服务器环境。使用的数据库连接参数也不同。为了更好的维护这些信息,我们建议把数据库连接信息提取到Mybatis全局配置文件外边。
创建属性文件
wechat.dev.driver=com.mysql.jdbc.Driver
wechat.dev.url=jdbc:mysql://192.168.198.100:3306/mybatis-example
wechat.dev.username=root
wechat.dev.password=atguigu
wechat.test.driver=com.mysql.jdbc.Driver
wechat.test.url=jdbc:mysql://192.168.198.150:3306/mybatis-example
wechat.test.username=root
wechat.test.password=atguigu
wechat.product.driver=com.mysql.jdbc.Driver
wechat.product.url=jdbc:mysql://192.168.198.200:3306/mybatis-example
wechat.product.username=root
wechat.product.password=atguigu
引入属性文件中的值
在
Mybatis
全局配置文件中指定外部
jdbc.properties
文件的位置。
<propertiesresource="jdbc.properties"/>
引用属性文件中的值
在需要具体属性值的时候使用${key}格式引用属性文件中的键
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息(引用了外部属性文件中的数据) -->
<property name="driver" value="${wechat.dev.driver}"/>
<property name="url" value="${wechat.dev.url}"/>
<property name="username" value="${wechat.dev.username}"/>
<property name="password" value="${wechat.dev.password}"/>
</dataSource>
3.2.3 用上Mapper接口
Mybatis中的
Mapper
接口相当于以前的Dao。但是区别在于,Mapper仅仅只是建接口即可,我们不需要提供实现类。该用法的思路如下图所示:
测试类中抽取代码
这一步和Mapper接口没关系,只是对代码本身的优化:
privateSqlSession session;// junit会在每一个@Test方法前执行@Before方法@Beforepublicvoidinit()throwsIOException{
session =newSqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")).openSession();}// junit会在每一个@Test方法后执行@After方法@Afterpublicvoidclear(){
session.commit();
session.close();}
声明Mapper接口
publicinterfaceEmployeeMapper{EmployeeselectEmployee(Integer empId);}
- 方法名和SQL的
id
一致; - 方法返回值和
resultType
一致; - 方法的参数和SQL的参数一致;
- 接口的全类名和映射配置文件的名称空间一致;
测试方法
@TestpublicvoidtestUsrMapperInterface(){// 1.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);// 2.调用EmployeeMapper接口的方法完成对数据库的操作Employee emp = employeeMapper.selectEmployee(1);// 3.打印查询结果if(emp !=null)
log.info(emp.toString());}
3.3 给SQL语句传参
3.3.1
#{}
形式
Mybatis会将SQL语句中的
#{}
转换为问号占位符。
3.3.2
${}
形式
${}
形式传参,底层Mybatis做的是字符串拼接操作。
通常不会采用
${}
的方式传值。 一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用
${}
。 结论:实际开发中,能用
#{}
实现的,肯定不用
${}
。
3.4 数据输入
3.4.1 MyBatis总体机制概括
3.4.2 概括说明
这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
- 简单类型:只包含一个值的数据类型 - 基本数据类型:int、byte、short、double、……- 基本数据类型的包装类型:Integer、Character、Double、……- 字符串类型:String
- 复杂类型:包含多个值的数据类型 - 实体类类型:Employee、Department、……- 集合类型:List、Set、Map、……- 数组类型:int[]、String[]、……- 复合类型:List、实体类中包含集合……
3.4.3 单个简单类型参数
抽象方法声明
EmployeeselectEmployee(Integer empId);
SQL语句
<selectid="selectEmployee"resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select>
单个简单类型参数,在
#{}
中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。
3.4.4 实体类型参数
抽象方法声明
intinsertEmployee(Employee employee);
SQL语句
<insertid="insertEmployee">
insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
</insert>
Mybatis会根据
#{}
中传入的数据,加工成
getXxx()
方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到
#{}
解析后的问号占位符这个位置。
3.4.5 零散的简单数据类型
抽象方法声明
intupdateEmployee(@Param("empId")Integer empId,@Param("empSalary")Double empSalary);
SQL语句
<updateid="updateEmployee">
update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}
</update>
3.4.6 Map类型参数
抽象方法声明
intupdateEmployeeByMap(Map<String,Object> paramMap);
SQL语句
<updateid="updateEmployeeByMap">
update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}
</update>
测试
@TestpublicvoidtestUpdateEmpNameByMap(){EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);Map<String,Object> paramMap =newHashMap<>();
paramMap.put("empSalaryKey",999.99);
paramMap.put("empIdKey",5);int result = mapper.updateEmployeeByMap(paramMap);
log.info("result = "+ result);}
使用场景
有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。
3.5 数据输出
数据输出总体上有两种形式:
- 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
- 查询操作的查询结果
3.5.1 返回单个简单类型数据
抽象方法
intselectEmpCount();
SQL语句
<selectid="selectEmpCount"resultType="int">
select count(*) from t_emp
</select>
Mybatis内部给常用的数据类型设定了很多别名。 以
int
类型为例,可以写的名称有:
int
、
integer
、
Integer
、
java.lang.Integer
、
Int
、
INT
、
INTEGER
等等。
3.5.2 返回实体类对象
抽象方法
EmployeeselectEmployee(Integer empId);
SQL语句
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 --><!-- resultType属性:指定封装查询结果的Java实体类的全类名 --><selectid="selectEmployee"resultType="com.atguigu.mybatis.entity.Employee"><!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 --><!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}
</select>
通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
增加配置自动识别对应关系
在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名
<!-- 在全局范围内对Mybatis进行配置 --><settings><!-- 具体配置 --><!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 --><!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 --><!-- 规则要求数据库表字段命名方式:单词_单词 --><!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 --><settingname="mapUnderscoreToCamelCase"value="true"/></settings>
3.5.3 返回Map类型
适用于SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中。能够封装成实体类类型的,就不使用Map类型。
抽象方法
Map<String,Object>selectEmpNameAndMaxSalary();
SQL语句
<!-- Map<String,Object> selectEmpNameAndMaxSalary(); --><!-- 返回工资最高的员工的姓名和他的工资 --><selectid="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>
3.5.4 返回List类型
查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
抽象方法
List<Employee>selectAll();
SQL语句
<!-- List<Employee> selectAll(); --><selectid="selectAll"resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp
</select>
3.5.5 返回自增主键
返回场景
例如:保存订单信息。需要保存
Order
对象和
List<OrderItem>
。其中,
OrderItem
对应的数据库表,包含一个外键,指向
Order
对应表的主键。
在保存
List<OrderItem>
的时候,需要使用下面的SQL:
insertinto t_order_item(item_name,item_price,item_count,order_id)values(...)
这里需要用到的order_id,是在保存Order对象时,数据库表以自增方式产生的,需要特殊办法拿到这个自增的主键值。至于,为什么不能通过查询最大主键的方式解决这个问题,参考下图:
SQL语句
<!-- int insertEmployee(Employee employee); --><!-- useGeneratedKeys属性字面意思就是“使用生成的主键” --><!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 --><insertid="insertEmployee"useGeneratedKeys="true"keyProperty="empId">
insert into t_emp(emp_name,emp_salary)
values(#{empName},#{empSalary})
</insert>
Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回
不支持自增主键的数据库
而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用
<insertid="insertEmployee"parameterType="com.atguigu.mybatis.beans.Employee"databaseId="oracle"><selectKeyorder="BEFORE"keyProperty="id"resultType="integer">
select employee_seq.nextval from dual
</selectKey>
insert into orcl_employee(id,last_name,email,gender) values(#{id},#{lastName},#{email},#{gender})
</insert>
或者是
<insertid="insertEmployee"parameterType="com.atguigu.mybatis.beans.Employee"databaseId="oracle"><selectKeyorder="AFTER"keyProperty="id"resultType="integer">
select employee_seq.currval from dual
</selectKey>
insert into orcl_employee(id,last_name,email,gender) values(employee_seq.nextval,#{lastName},#{email},#{gender})
</insert>
3.5.6 使用resultMap
使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 --><resultMapid="selectEmployeeByRMResultMap"type="com.atguigu.mybatis.entity.Employee"><!-- 使用id标签设置主键列和主键属性之间的对应关系 --><!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 --><idcolumn="emp_id"property="empId"/><!-- 使用result标签设置普通字段和Java实体类属性之间的关系 --><resultcolumn="emp_name"property="empName"/><resultcolumn="emp_salary"property="empSalary"/></resultMap><!-- Employee selectEmployeeByRM(Integer empId); --><selectid="selectEmployeeByRM"resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>
版权归原作者 一棵___大树 所有, 如有侵权,请联系我们删除。