${..}和#{..} 占位符
#{..}
#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?。
<!-- 根据id查询用户信息 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
使用占位符#{}可以有效防止sql攻击
- 使用#{} 防止SQL注入攻击的底层代码如下
// SQL查询语句,使用占位符 ?
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
try (Connection conn = DriverManager.getConnection(dbURL, username, password)) {
if (conn != null) {
// 创建PreparedStatement对象
PreparedStatement pstmt = conn.prepareStatement(query);
// 设置参数
pstmt.setString(1, userInputUsername); // 第一个占位符
pstmt.setString(2, userInputPassword); // 第二个占位符
通过上面的代码可以发现,将 传递的参数 和 sql 语句进行分离。在预编译阶段,sql 语句作为创建 preparedStatement 对象的参数,就已经确定好了。因此即使用户 进行sql 注入攻击 ,无论你输入什么,都当成一般的数据。而不会被拼接到sql 语句去。成为新的sql 语句。
什么是sql 攻击呢?
假设当前是登录界面(不使用#{} 可能会遭受 sql 攻击)
不管你之前输入了,什么,如果你后面添加这种 or 1=1 这种类似的
最后拼接到查询的sql 语句中,即使之前输入错误也可以成功。这就是因为 sql 注入导致的sql 攻击
使用#{..} 的好处
1 预防sql 攻击
2 在 mybatis 框架 映射文件,不需要手动为 传递的数据为字符串 ,添加单引号。最会实现自动添加
${..}
应用场景:${..},占位符 多用于 字符串拼接
${..} 和 #{..} 的区别
${..} 无法预防sql 攻击,当传递参数后,会直接 和 sql 语句 进行拼接,改变原有sql 语句所表达的含义
${..} 在模糊查询中,效果更大
案例
- ${..}和#{..} 使用模糊查询,查询数据,在 映射文件中的sql语句,测试类传递的参数 表现不同的形式
项目准备
目的:基于mybatis 框架 基础 使用 模糊查询 查 关于 l 的所有用户信息
使用 #{} 在 映射文件 的sql 语句
select * from tb_login where username like #{username};
测试类
List<Login> list = sqlSession.selectList("getLoginByM", "%l%");
for (Login login : list) {
System.out.println("id: "+login.getId()+"用户名: "+login.getUsername()+"密码: "+login.getPassword());
}
效果展示
使用 ${} 在 映射文件 的sql 语句
select * from tb_login where username like'%${m}%';
测试类
List<Login> list = sqlSession.selectList("getLoginByM", "l");
for (Login login : list) {
System.out.println("id: "+login.getId()+"用户名: "+login.getUsername()+"密码: "+login.getPassword());
}
效果展示
发现 在上面使用 #{..} 传递参数 时需要添加,%;而 ${..} 因为在书写 sql 语句就已经拼接好 %,因此传递参数时,就不再需要添加
resultType 和 resultMap
1. resultType
的使用场景
定义:
resultType
是 MyBatis 中用于指定查询结果映射到的 Java 类型。特点:
简单映射:
resultType
通常用于简单的情况,比如查询结果直接映射到基本数据类型(如int
、String
、Date
等)或简单对象(如User
、Product
等)。限制:当查询结果的列名与实体类的属性名完全匹配时,可以直接使用
resultType
。如果查询结果的列名和实体类的属性名不匹配,或者查询结果包含嵌套对象(如关联对象),则不能直接使用resultType
。不支持复杂映射:
resultType
不支持复杂的结果映射,比如嵌套对象、多表关联查询等。
2. resultMap
的使用场景
定义:
resultMap
是 MyBatis 中用于定义复杂的结果映射规则。特点:
复杂映射:
resultMap
可以处理复杂的情况,比如多表关联查询、嵌套对象、嵌套集合等。灵活性:通过
resultMap
,可以定义列名到属性名的映射规则,支持嵌套结果映射(如一对一、一对多关系)。适用性:
resultMap
适用于复杂查询,尤其是当查询结果的列名与实体类的属性名不匹配,或者查询结果包含嵌套对象时。
resultType 和 resultMap 的区别
1resultType
适用于简单查询,要求查询结果的列名与实体类的属性名完全匹配,不支持复杂的结果映射(如嵌套对象)。与之相反,resultMap
可以处理复杂的情况,支持嵌套对象、嵌套集合等,适用于多表关联查询等复杂场景
2 使用 resultType 要求 实体类和 数据库之前 需要存在映射关系,如果不是则无法将数据,传递给实体类的成员变量。而 resultMap 不必遵循这种关系
通过图片,可知 数据库的列名和成员变量要求保持一致,在使用 resultType
3 resultMap的使用
应用场景:假设是订单和用户的关系。一个用户对应多个订单;一个订单对应一个用户。在实体类 user 类 和 实体类对应的映射文件 描述这种情况
demo(案例)
User 类
Product 类
mapper/dao UserMapper接口
// 查询用户 所有商品信息
List<Product> queryProduct(User user);
UserMapper.xml映射文件
<resultMap id="order" type="fs.entity.Order">
<!-- id 标签 表示为一个表的主键 column 表示为表中的列名 ,property 表示为 类中的属性名 -->
<id property="id" column="id"/>
<!-- result 标签 表示一般的字段 -->
<result property="name" column="name"/>
<result property="price" column="price"/>
<!--collection 标签 使用 <collection> 处理一对多关系 ,使用 <association> 处理一对一关系 -->
<collection property="products" ofType="fs.entity.Product">
<id property="id" column="pid"/>
<result property="name" column="pname"/>
<result property="price" column="pprice"/>
<!-- <!-
collection:对关联查询到的多条记录映射到集合对象中
property:将查询到的多条记录映射到User类的那个属性中
ofType: 指明集合中的元素的类型
-->
</collection>
</resultMap>
id: 唯一标识此 resultMap。
type: 指定要映射到的 Java 类型(可以是全限定名或别名)。
<id> 和 <result> 子元素分别用于映射主键和普通属性。property 属性对应于 Java Bean 的属性名,column 属性对应于 SQL 查询结果中的列名。collection 标签 使用 <collection> 处理一对多关系 ,使用 <association> 处理一对一关系 association association:用于映射关联单个对象信息 property:关联的属性名,就是将用户对象关联到Order的那个属性 javaType: 属性的类型
collection
collection:对关联查询到的多条记录映射到集合对象中
property:将查询到的多条记录映射到User类的那个属性中
ofType: 指明集合中的元素的类型
使用 resultMap
在查询语句中引用这个 resultMap
<select id="queryProduct" parameterType="fs.entity.User" resultMap="order">
// sql 语句
</select>
typeAliases(类型别名) 的使用
mybatis支持别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
自定义别名
在mybatis-config.xml中配置:
<typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="org.csmf.mybatis.entity.User"/> <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) --> <package name=" org.csmf.mybatis.entity "/> <package name="其它包"/> </typeAliases>
使用别名:
Mapper 代理模式
了解 原始Dao开发方法
想要了解 Mapper 代理模式 ,就要先知道 原始Dao开发方法
使用Mybatis开发Dao,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。
传统的DAO开发模式
dao/mapper 持久层接口中的方法
/**
* 根据ID查询User
* @param id 用户Id
* @return 用户信息
* @throws Exception
*/
public User findUserById(int id) throws Exception;
dao/mapper 持久层 接口实现 类
//需要往UserDaoImpl注入SQLSessionFactory
private SqlSessionFactory sqlSessionFactory;
//通过构造方法注入
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
/**
* 1.得到SqlSession
* 2.调用SqlSession对应的方法(selectOne)来操作数据库
* 3.关闭SqlSession
* 4.返回结果
*/
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.close();
return user;
}
发现如果在接口中写很多关于操作数据库的方法,那么在接口的实现类中,每次都需要创建sqlSession 对象。基于代码的重复,于是使用依靠 Java动态代理技术的mapper 代理模式
mapper 代理模式
mapper 代理模式 ,被mybatis 官方所推崇。因此 很多情况 实现 dao/mapper持久层 的操作 都是使用 mapper 代理模式
mapper 代理模式 的优点
1 mapper 代理模式 省略了 接口的实现类,使用 映射文件 书写需要的sql 语句,具体如何去实现接口的方法有代理对象调用
2 代理类去实现对应的接口,调用 相关方法,这些操作都已经封装好了。不需要人为创建代理类,完成这些操作,更加简单,方便
mapper 代理模式 的原则
1 在mapper.xml中namespace写的是对应Mapper接口的全限定名
-
Mapper接口方法名和Mapper.xml中定义的每个标签如select,update等中的id相同
-
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同