当前位置: 首页 > news >正文

MyBatis极速通关中篇:核心配置精讲与复杂查询实战

核心配置文件讲解

核心配置文件通常命名为mybatis-config.xml(这是规范但不强制要求),核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱): properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers

<?xml version="1.0" encoding="UTF-8" ?><!--文档类型配置 定义当前文档的元素-->
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><!--配置标签 根元素-->
<configuration><!--指定当前xml文件读取的属性配置文件信息,db.properties里面是数据库连接基本信息--><properties resource="db.properties" /><settings><!-- 启用日志 --><setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        懒加载--><setting name="lazyLoadingEnabled" value="true"/><!-- 开启驼峰映射 ,为自定义的SQL语句服务--><setting name="mapUnderscoreToCamelCase" value="true"/></settings><typeAliases>
<!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写--><package name="com.cjh.pojo"/></typeAliases><!--环境配置--><environments default="development"><!--当前使用的开发环境使用的数据源配置信息--><environment id="development"><!--事务管理器 MyBatis不处理事务 表示由JDBC来控制事务--><transactionManager type="JDBC"/><!--数据源 使用MyBatis内置的连接池技术--><dataSource type="POOLED"><!--name值不可更改,我们需要写的是value值,value值分别对应db.properties文件里的信息--><property name="driver" value="${DRIVER}"/><property name="url" value="${URL}"/><property name="username" value="${USERNAME}"/><property name="password" value="${PASSWORD}"/></dataSource></environment></environments><!--映射器配置 指定MyBatis的Mapper接口位置--><mappers><!--配置包扫描 会自动扫描该包下的所有Mapper接口--><package name="com.cjh.mapper"/></mappers>
</configuration>

Mybatis的增删改查

要通过mybatis框架实现对数据库的增删改查,操作如下

mybatis的特点是面向接口编程,所以我们要在接口层定义接口和接口方法
举例现在有一张表user,表字段有name,password,我们要实现插入user数据的功能
首先创建实体类user映射表字段(省略详细操作),然后我们就要在接口层定义一个接口userMapper(接口名字通常为表名+Mapper),然后再定义一个接口方法

public interface UserMapper {
//增删改的接口方法一般返回值都是int ,返回值>0则操作成功,反之操作失败int addUser(User user);
}

如果我们的接口方法有多个参数,但我们还是直接按以下方法定义接口的话:

int addUser2(String name, String password);

当我们在测试功能的时候就会出现这样的报错:

这是为什么呢?因为mybatis底层其实是吧我们传入的name值和password值存入一个map集合里,按照顺序的话,这个集合里第一个键值对就是:key arg0  value name值  key arg1 value password值
所以底层其实是通过map集合键来获取 我们传入的值,我们要么按照底层框架的命名在sql语句里使用原本框架定义的键值,但是这样不能见名知意,所以我们一般采取通过注解给键值起别名的方式来优化

我们要给每个参数加一个注解,并设置注解的value值为参数名,例如:

int addUser2(@Param("name") String name, @Param("password") String password);

Mybatis有高级映射的特点,我们既可以再接口方法上添加注解直接书写sql语句,也可以通过xml映射文件。一般来说简单sql语句直接用注解书写比较方便,接下来我演示一下

public interface UserMapper {@Insert("insert into user(name,password) values(#{name},#{password})")int addUser(User user);
}

接着在演示另一种方法前先来简单讲解一下xml映射文件

MyBatis 映射文件(Mapper XML)是 MyBatis 框架中的核心组件,它充当了数据库操作与 Java 代码之间的桥梁

映射文件是 XML 格式的配置文件(通常命名为 XxxMapper.xml),它定义了:

1.Java 对象与数据库表之间的映射关系

2.SQL 语句的具体实现细节

3.SQL 执行时的参数设置

4.SQL 返回结果的处理规则

接下来我们讲一下具体操作,通过实际操作给你讲解一下MyBatis 映射文件的用法和规则,mybatis的命名规则是对应实体类类名+Mapper.xml,在本例中实体类是User,所以我们要创建一个UserMapper.xml文件,文件初始内容如下,顶部XML 片段是 MyBatis 映射文件的头部声明(不许要我们自己写,直接找官方复制就好),接下来的mapper标签才是我们编写sql语句的主场,标签里的namespace属性的值对应的是我们定义的UserMapper接口的全类名,你可以理解成用来连接这个接口

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.cjh.mapper.UserMapper"></mapper>

接下来我们就要在mapper标签内书写我们的sql语句,sql操作分为增删改查,对应的标签也是如此,实现插入操作,首先定义一个insert 标签,这个标签必须赋值的属性就是id,id的值就是我们在UserMapper定义的接口方法,sql语句里user(name,password)里的name和password对应的是user表里的字段(必须保持一致),而#{name},#{password} 里的#{}你可以理解为传入的参数,我们定义接口方法不是设置传入一个User对象吗,而这个name,password就对应User的两个属性名
name,password(必须保持一致!!!)

后面更加复杂的操作还有其他标签属性我再一一讲解,目前这是最基础的语法要求

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.cjh.mapper.UserMapper"><insert id="addUser">insert into user(name,password) values(#{name},#{password})</insert>
</mapper>

准备工作完成后我们就开始测试插入数据了
要想用Java语言操作数据库,我们需要要获取java与数据库的会话对象,再用反射+动态代理的方法获取接口的对象,然后调用接口方法
首先我们要调用Resource的getResourceAsStream读取mybatis-config.xml文件里的配置信息,然后返回一个输入流

InputStream ins = Resources.getResourceAsStream("mybatis-config.xml");

然后我们传入ins参数来创建一个会话工厂对象sqlSessionFactory用于生产会话对象sqlSession

 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ins);

接着调用sqlSessionFactory的openSession()方法获取会话对象sqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();

然后通过会话对象的getMapper方法获取我们要的接口对象

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

接着调用接口方法传入参数实现插入数据的操作

int result =mapper.addUser(new User("cjh", "123456"));

最后提交事务

sqlSession.commit();

完整代码如下:

 InputStream ins = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ins);
// 推荐写法(包含事务和资源管理)
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);int rows = mapper.addUser(new User("cjh", "123456"));System.out.println("插入记录数: " + rows);sqlSession.commit(); // 手动提交
} // 自动关闭sqlSession

运行结果:

自定义工具类(工厂模式)

上面的代码就是我们实现插入操作的完整步骤了,大家会不会觉得有些繁琐了,每次都要新建各种对象最终只为了获取会话对象sqlsession,不仅麻烦而且浪费资源,所有操作同一个数据库的功能获取sqlsession对象的代码都一模一样,而且他们只需要一个工厂对象来生产sqlsession即可,所以我们可以把这些代码抽取出来,通过设计工具类方法来实现获取SqlSession对象,这样简直优雅!优雅!优雅!具体实现如下:

package com.cjh.util;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 MybatisUtil {private static  SqlSessionFactory sqlSessionFactory;static{InputStream ins  ;try {ins= Resources.getResourceAsStream("mybatis-config.xml");sqlSessionFactory = new SqlSessionFactoryBuilder().build(ins);} catch (IOException e) {throw new RuntimeException(e);}}public static SqlSession getSqlSession(){return sqlSessionFactory.openSession(true);}public static void close(SqlSession sqlSession){if(sqlSession!=null){sqlSession.close();}}
}

接下来就是优雅的代码了:

  SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);int rows = mapper.addUser(new User("cjh", "123456"));

增删改操作其实都类似,在这里我就不一一讲解了,我重点讲解一下查询。

查询的返回值类型有很多,根据业务需求而定,通常如果是只查询一条数据的话返回值就是一个实体类,多条数据就是集合。

举例比如我们现在要查询uesr表上的所有数据,接口方法就要这样定义:

 List<User> getAllUser();

映射文件如下,注意查询的标签与增删改这里有所不同,select标签必须填写resultType属性,属性值就是返回值的全类名

  <select id="getAllUser" resultType="com.cjh.pojo.User">select * from user</select>

测试代码如下:

    public static void main(String[] args) {SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users =mapper.getAllUser();users.forEach(System.out::println);}
}

运行结果:

自定义映射resultMap

上面讲解的都是最简单的场景实际开发中的情况可能要复杂的多,我们可能会遇到实体类属性不能一一对应表字段的情况,还可能会遇到要联表查询的问题·,因此我们需要用到自定义映射resultMap,这也是mybatis区别于传统jdbc的核心所在————高级映射


实体类属性名不能一一对应表字段名

在一些场景下,我们的实体类的属性名并不能和表字段名一致,比如说有个表字段名是user_name,但是我们实体类的属性名如果要定义成user_name显然不太规范和优雅,通常我们定义的是属性名会是userName,那我们想要让userName去映射表字段user_name要怎么做呢?那我们就不能按照默认规则来,就要自己定义一套规则来告诉jvm底层我们要让实体类属性userName映射表字段名user_name,这时候我们就要用到自定义映射resultMap

举例我们现在有实体类:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {private int id;private String userName;private String passWord;
}

表:user  表字段:user_name ,user_password  ,主键 id

那我们的映射文件就要这么写:

其中resultMap标签就是用来自定义映射内容的,resultMapde的id属性值是用来声明标签的以便其他标签引用,type属性值是要映射的表的实体类的全类名。

id标签是设置主键的映射关系  id标签的column属性值是对应的user表主键名,property是映射的实体类属性名
result标签是设置普通字段的映射关系, result标签的column属性值是对应的user表字段名,property是映射的实体类属性名

  <resultMap id="aaa" type="com.cjh.pojo.User"><id column="id" property="id"/><result column="user_name" property="userName"/><result column="user_password" property="passWord"/></resultMap>

最后我们在sql标签里引用我们自定义的映射resultMap就可以了(吧sql标签内的resultMap属性值设为我们定义好的resultMap的id值)

 <select id="getUserByName" resultType="com.cjh.pojo.User" resultMap="aaa">select * from user where user_name = #{userName}</select>

联表查询

如果我们要查询的是表字段涵盖多张表呢,那我们的实体类属性也要随之发生变化,比如现在有两张表,分别是

customer(客户表):

record(咨询部,用来记录客户的咨询记录):

一对多

一对多的意思是,一个客户可以有多条咨询记录,而一条咨询记录只能对应一个客户,在一对多里客户就是一的一方,我们如果想通过客户携带出咨询记录的信息那必然会携带出多条咨询记录,所以我们要给客户的实体类增加一个类型为集合,泛型为Record的属性用来封装多条咨询记录,具体操作如下:

举例:现在要获取所有客户信息以及每个客户对应的咨询信息

1.实体类添加属性records(重点)建立对象关联

package com.cjh.pojo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Customer {private Integer id;private String name;private String sex;private Integer age;private String createtime;private List<Record> records;
}

2.定义接口方法,与之前类似,有多条数据就用List集合返回值

List<Customer> getAllRecord();

3.编写映射文件(重点):
我们依然需要采取自定义映射的方式,首先(配置本表的属性映射,也就是一对多里的一那一方)原本Customer类的属性id,name,sex,age,createtime映射还和之前一样,但是新加的属性records映射有所不同,因为这个属性的类型是集合而且这个属性对应的本身就是另外一张表了,我们要用一个collection标签来实现 标签属性property的值是我们新加的属性名records,ofType标签的属性值是集合存储的数据类型也就是泛型

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.cjh.mapper.CustomerMapper"><resultMap id="customerMap" type="com.cjh.pojo.Customer"><id column="id" property="id"/><result column="name" property="name"/><result column="sex" property="sex"/><result column="age" property="age"/><result column="createtime" property="createtime"/><collection property="records" ofType="record"><id column="r_id" property="id"/><result column="desp" property="desp"/><result column="grade" property="grade"/><result column="cid" property="cid"/><result column="eid" property="eid"/><result column="recordtime" property="recordtime"/></collection></resultMap><select id="getAllRecord" resultType="com.cjh.pojo.Customer" resultMap="customerMap">select r.id as r_id,r.desp,r.grade,r.cid,r.eid,r.recordtime, customer.* from customer left join crm_db.record r on customer.id = r.cid</select>
</mapper>

多对一

站在客户角度,一个客户有多条咨询记录,那就是多对一。站在咨询记录角度,多条咨询记录对应一个客户,那就是多对一。
那如果我们想要通过咨询记录携带出客户信息(实现关联查询)要怎么做呢?已知一条咨询记录只能对应一个客户,那我们就不需要用集合了。我们可以通过给咨询表的实体类增加一个数据类型为客户类,属性名与客户类同名的属性即可。

举例比如我们要查询每条咨询记录对应的客户信息,我们可以这样做

1.实体类增加属性customer(建立对象关联)

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Record {private Integer id;private String desp;private Integer grade ;private Integer cid;private Integer eid;private LocalDateTime recordtime;private Customer customer;
}

2.定义接口方法

 List<Record> getRecordAndNameByEid(int i);

3.编写映射文件
其余步骤与一对多一致,但是在”多“的一方的表映射要用association标签,标签属性property对应实体类属性名,javaType对应实体类的全类名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.cjh.mapper.RecordMapper"><resultMap id="recordWithCustomerNameMap" type="com.cjh.pojo.Record"><id column="id" property="id"/><result column="desp" property="desp"/><result column="grade" property="grade"/><result column="cid" property="cid"/><result column="eid" property="eid"/><result column="recordtime" property="recordtime"/><association property="customer" javaType="com.cjh.pojo.Customer"><result column="name" property="name"/></association></resultMap><select id="getRecordAndNameByCid" resultType="com.cjh.pojo.Record"  resultMap="recordWithCustomerNameMap">select record.*, customer.name from record left join customer on record.cid = customer.id where record.cid = #{i}</select>
</mapper>

http://www.lryc.cn/news/622861.html

相关文章:

  • 大模型教机器人叠衣服:2025年”语言理解+多模态融合“的智能新篇
  • Tomcat架构深度解析:从Server到Servlet的全流程揭秘
  • blender制作动画导入unity两种方式
  • ENSP的简单动态路由rip协议配置
  • 广东省省考备考(第七十八天8.16)——资料分析、判断推理(强化训练)
  • Docker目录的迁移
  • GaussDB 数据库架构师修炼(十三)安全管理(3)-行级访问控制
  • 6JSON格式转python并实现数据可视化
  • 在ubuntu系统上离线安装jenkins的做法
  • 零基础学习人工智能的完整路线规划
  • Flink Stream API 源码走读 - window 和 sum
  • (第十七期)HTML图像标签详解:从入门到精通
  • 【完整源码+数据集+部署教程】高尔夫球追踪与识别系统源码和数据集:改进yolo11-LAWDS
  • 【基础-判断】可以通过ohpm uninstall 指令下载指定的三方库
  • 力扣(接雨水)——基于最高柱分割的双指针
  • 【开发技巧】VS2022+QT5+OpenCV4.10开发环境搭建QT Creator
  • 肖臻《区块链技术与应用》第23-26讲 - The DAO事件、BEC事件、反思和总结
  • Qt 关于QString和std::string数据截断的问题- 遇到\0或者0x00如何处理?
  • ★CentOS:MySQL数据备份
  • 三天速通 Vue+Flask+SQLite 项目+阿里云轻量应用级服务器【宝塔面板】②
  • 数学建模Topsis法笔记
  • TOGAF八步一法笔记2
  • 【DL学习笔记】常用数据集总结
  • OpenShift 4.19安装中的变化
  • 民法学学习笔记(个人向) Part.5
  • Protues使用说明及Protues与Keil联合仿真实现点亮小灯和流水灯
  • 【运维心得】三步更换HP笔记本电脑外壳
  • C++基础——内存管理
  • C++实战
  • 《深度解构:构建浏览器端Redis控制台的WebSocket协议核心技术》