软件设计之SSM(8)
路线图推荐:
【Java学习路线-极速版】【Java架构师技术图谱】
尚硅谷新版SSM框架全套视频教程,Spring6+SpringBoot3最新SSM企业级开发
资料可以去尚硅谷官网免费领取
学习内容:
Mybatis
- 数据输出
- 多表映射
- 动态语句
1、数据输出
数据输出总体上有两种形式:
- 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
- 查询操作的查询结果
单个简单类型
指定返回值类型就是指定resultType
- 类的全限定符号
<select id = "queryNameById" resultType ="java.lang.String">
</select>
- 别名简称
mybatis 提供了72种默认别名,基于常用的数据类型
- 基本数据类型
int
、double
->_int
、_double
- 包装数据类型
Integer
、Double
->int
、double
- 集合容器类型
Map
、List
->map
、list
- 自定别名
<!-- 给类单独定义别名-->
<typeAliases>
<typeAlias alias="worker" type="com.atguigu.Employee"/>
</typeAliases>
<!-- 批量将包下的类定义别名,别名是类首字母小写-->
<typeAliases>
<package name="com.atguigu.pojo"/>
</typeAliases>
实体对象类型
在 MyBatis 中,返回单个实体类型时,“列名与属性名一致” 是指:数据库查询结果中的列名需要与 Java 实体类(POJO)中的属性名相匹配
,以便 MyBatis 能够正确地将 SQL 查询结果映射为 Java 对象。
在mybatis-config.xml
设置里,支持驼峰式自动映射。
即: emp_id --> empId
<!-- 在全局范围内对Mybatis进行配置 -->
<settings>
<!-- 具体配置 -->
<!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
<!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
<!-- 规则要求数据库表字段命名方式:单词_单词 -->
<!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
List类型
查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可
List<Employee> selectAll();
<!-- List<Employee> selectAll(); -->
<select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp
</select>
返回主键值
主键回显:
是指在数据库执行插入操作时,数据库会自动生成并返回插入数据的主键值- 自增长类型主键
<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">
insert into t_emp(emp_name,emp_salary)
values(#{empName},#{empSalary})
</insert>
- 非自增长类型主键
插入 UUID 作为字符串类型主键
<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" resultType="java.lang.String"
order="BEFORE">
SELECT UUID() as id
</selectKey>
INSERT INTO user (id, username, password)
VALUES (
#{id},
#{username},
#{password}
)
</insert>
ResultMap自定义映射
实体类属性和数据库字段对应
使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">
<!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
<!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
<id column="emp_id" property="empId"/>
<!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
<result column="emp_name" property="empName"/>
<result column="emp_salary" property="empSalary"/>
</resultMap>
<!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>
2、多表映射
实体类设计
对一,属性中包含对方对象
对多,属性中包含对方对象集合
只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类!
无论多少张表联查,实体类设计都是两两考虑!
在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和对应的客户,就不要关注客户中的订单集合!
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList;// 体现的是对多的关系
}
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
//查询客户和客户对应的订单集合 不要管!
对一映射
在MyBatis中实现多表映射时,常见的场景之一是对一映射,即一个实体类关联另一个实体类的情况。这通常用于像“用户-地址”这样的关系。实现步骤主要包括:
假设有两个表 User 和 Address,用户表(User)
和地址表(Address)
之间存在一对一的关系。
流程:
UserMapper.xml:
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="UserResultMap" type="com.example.domain.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<!-- 对一映射 -->
<association property="address" javaType="com.example.domain.Address">
<id property="id" column="address_id"/>
<result property="street" column="street"/>
<result property="city" column="city"/>
</association>
</resultMap>
<!-- 查询语句 -->
<select id="selectUserById" resultMap="UserResultMap">
SELECT u.id AS user_id, u.name AS user_name, a.id AS address_id, a.street, a.city
FROM User u
JOIN Address a ON u.address_id = a.id
WHERE u.id = #{id};
</select>
</mapper>
对多映射
在MyBatis中,对多映射(一对多)通常用于表示“一个实体类包含多个子类”的关系,比如“一个用户有多个订单”的场景。MyBatis 提供了collection
标签来处理这种一对多映射。
假设有两个表:User 和 Order,用户(User)
和订单(Order)
之间是一对多的关系。
流程:
UserMapper.xml:
<mapper namespace="com.example.mapper.UserMapper">
<!-- 配置 UserResultMap -->
<resultMap id="UserResultMap" type="com.example.domain.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<!-- 一对多映射 -->
<collection property="orders" ofType="com.example.domain.Order">
<id property="id" column="order_id"/>
<result property="orderNumber" column="order_number"/>
<result property="userId" column="user_id"/>
</collection>
</resultMap>
<!-- 查询语句 -->
<select id="selectUserById" resultMap="UserResultMap">
SELECT u.id AS user_id, u.name AS user_name, o.id AS order_id, o.order_number, o.user_id
FROM User u
LEFT JOIN `Order` o ON u.id = o.user_id
WHERE u.id = #{id};
</select>
</mapper>
3、动态语句
JDBC字符串拼接
繁琐: 随着查询条件的增加,SQL 语句的拼接会变得复杂,代码不易维护。
易错: 手动拼接容易遗漏空格、标点等,导致语法错误。
安全问题: 手动拼接 SQL 会引发 SQL 注入风险,如果参数未经正确处理,攻击者可以插入恶意 SQL。
String sql = "SELECT * FROM User WHERE 1=1";
if (name != null) {
sql += " AND name = '" + name + "'"; // 手动拼接参数,易引发 SQL 注入风险
}
if (age != null) {
sql += " AND age = " + age;
}
PreparedStatement ps = conn.prepareStatement(sql);
if和where标签
<if>
标签用于根据传入的参数或条件决定是否生成某段 SQL 语句。它通常与条件判断结合使用,能够根据不同的输入动态调整查询、插入或更新的 SQL 逻辑。
<where>
标签主要用于动态构建 WHERE 子句,它不仅能动态生成条件,还能自动处理 SQL 语句开头的逻辑连接符(如 AND、OR 等)。如果在没有任何条件时,<where>
标签会去除 SQL 中无效的 WHERE 子句。
<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="employee">
select emp_id,emp_name,emp_salary from t_emp
<!-- where标签会自动去掉“标签体内前面多余的and/or” -->
<where>
<!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
<!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
<if test="empName != null">
<!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
or emp_name=#{empName}
</if>
<if test="empSalary > 2000">
or emp_salary>#{empSalary}
</if>
<!--
第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
第二种情况:部分条件满足 WHERE emp_salary>?
第三种情况:所有条件都不满足 没有where子句
-->
</where>
</select>
foreach标签
在 MyBatis 中,<foreach>
标签用于处理 SQL 语句中的批量操作,例如批量插入、批量更新或者在 IN 子句中传递多个参数。它能够遍历集合或数组,并动态生成 SQL 语句,避免手动拼接多个参数,代码更加简洁、可维护。
<select id="selectUsersByIds" resultType="User">
SELECT * FROM User WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
参数传递:
List<Integer> ids = Arrays.asList(1, 2, 3, 4);
List<User> users = userMapper.selectUsersByIds(ids);
生成的 SQL 语句为:
SELECT * FROM User WHERE id IN (1, 2, 3, 4);