MyBatis-Plus Zero 学习笔记
凌余阵兮躐余行,左骖殪兮右刃伤。
Mybatis Zero
查询
Mybatispuls查询指定字段的方式
LambdaQueryWrapper<BrandEntity> brandWrapper = new LambdaQueryWrapper<>();
brandWrapper.select(BrandEntity::getName).eq(BrandEntity::getBrandId, categoryBrandRelation.getBrandId());
BrandEntity brand = brandService.getOne(brandWrapper);
log.info("brand: {}", brand);
brand: BrandEntity(brandId=null, name=华为, logo=null, descript=null, showStatus=null, sort=null, firstLetter=null)
插入前填充字段—selectKey 标签
说明
对于不支持自动生成主键列的数据库和可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。
这里有一个简单(也很傻)的示例,它可以生成一个随机 ID(不建议实际使用,这里只是为了展示 MyBatis 处理问题的灵活性和宽容度):
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>
在上面的示例中,首先会运行 selectKey 元素中的语句,并设置 Author 的 id,然后才会调用插入语句。这样就实现了数据库自动生成主键类似的行为,同时保持了 Java 代码的简洁。
selectKey 元素描述如下:
<selectKey
keyProperty="id"
resultType="int"
order="BEFORE"
statementType="PREPARED">
属性 | 描述 |
---|---|
keyProperty |
selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
resultType |
结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。 |
order |
可以设置为 BEFORE 或 AFTER 。如果设置为 BEFORE ,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER ,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 |
statementType |
和前面一样,MyBatis 支持 STATEMENT ,PREPARED 和 CALLABLE 类型的映射语句,分别代表 Statement , PreparedStatement 和 CallableStatement 类型。 |
案例
注意点
占位符(#、$)
- #{} 参数占位符
#占位符的特点
#{}
是 sql 的参数占位符- MyBatis处理 #{ } 占位符,使用的 JDBC 对象是 PreparedStatement 对象,执行sql语句的效率更高。
- 使用 PreparedStatement 对象,能够避免 sql 注入,使得sql语句的执行更加安全。
- #{ } 常常作为列值使用,位于sql语句中等号的右侧;#{ } 位置的值与数据类型是相关的。
使用 #{ } 对数据库执行 update 操作
package com.bjpowernode.dao;
import com.bjpowernode.entity.Student;
public interface StudentDao {
//更新学生信息
int updateStudent(Student student);
}
<!-- 更新学生信息 -->
<update id="updateStudent">
update student set name=#{name},email=#{email} where id=#{id}
</update>
@Test
public void testUpdateStudent() {
SqlSession session=MyBatisUtil.getSqlSession();
StudentDao studentDao=session.getMapper(StudentDao.class);
Student student=new Student();
student.setId(1003);
student.setName("最强王者");
student.setEmail("123456@qq.com");
student.setAge(28);
int rows=studentDao.updateStudent(student);
session.commit();
System.out.println("更新学生的rows === " + rows);
session.close();
}
- $占位符
$占位符的特点
${}
是 Properties 文件中的变量占位符- MyBatis处理 ${ } 占位符,使用的 JDBC 对象是 Statement 对象,执行sql语句的效率相对于 #{ } 占位符要更低。
- ${ } 占位符的值,使用的是字符串连接的方式,有 sql 注入的风险,同时也存在代码安全的问题。
- ${ } 占位符中的数据是原模原样的,不会区分数据类型。
- ${} 占位符常用作表名或列名,这里推荐在能保证数据安全的情况下使用 ${ }。
使用 ${ } 对数据库执行 select 操作
package com.bjpowernode.dao;
import com.bjpowernode.entity.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface StudentDao {
//${}占位符的使用,使用@Param命名参数
List<Student> queryStudent(@Param("studentName") String name);
}
<!-- ${}占位符的使用 -->
<select id="queryStudent" resultType="com.bjpowernode.entity.Student">
select * from student where name=${studentName}
</select>
@Test
public void testQueryStudent() {
SqlSession session= MyBatisUtil.getSqlSession();
StudentDao studentDao=session.getMapper(StudentDao.class);
List<Student> students=studentDao.queryStudent("'张起灵'");
for(Student stu : students) {
System.out.println("stu === " + stu);
}
session.close();
}
这里注意代码中通过 ${ } 占位符传值的地方,如果我们写成下面这样,代码运行一定是会报错的!!!
List<Student> students=studentDao.queryStudent("张起灵");
这是因为 ${ } 占位符中的数据是原模原样的,不会区分数据类型。也就是说它会把你的mapper文件中的sql语句转换为:
select * from student where name=张起灵 (错误!!!)
对sql语句有了解的人肯定都能看出这里的错误,首先我们的name字段的数据类型是 varchar,而这里 name=张起灵 显然是错误的,应该是 name= ‘张起灵’ 这样才对,所以代码中通过 ${ } 占位符传值的地方,应该这样写:👇👇👇
List<Student> students=studentDao.queryStudent("'张起灵'");
对参数 张起灵 添加引号,根据 ${ } 占位符中的数据是原模原样的,此时它会把你的mapper文件中的sql语句转换为:
select * from student where name='张起灵' (正确!!!)
- #{ }、${ } 占位符的综合使用
上面大致介绍了一下这两种占位符的使用规则和语法,下面再用一个综合的例子来应用一下这两种占位符吧!!!
package com.bjpowernode.dao;
import com.bjpowernode.entity.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface StudentDao {
List<Student> queryStudentOrderByColName(@Param("myName") String myName,
@Param("colName") String colName,
@Param("tableName") String tableName);
}
<!-- 按照某个列排序 -->
<select id="queryStudentOrderByColName" resultType="com.bjpowernode.entity.Student">
select * from ${tableName} where name=#{myName} order by ${colName} desc
</select>
@Test
public void testQueryStudentOrderByColName() {
SqlSession session= MyBatisUtil.getSqlSession();
StudentDao studentDao=session.getMapper(StudentDao.class);
List<Student> students=studentDao.queryStudentOrderByColName("张起灵","id","student");
for(Student stu : students) {
System.out.println("stu === " + stu);
}
session.close();
}
上述sql语句的意思是:从 student 表中查询 name=”张起灵” 的所有字段内容,并把结果按照 id 列进行降序排序。
- ${} 占位符的 SQL 注入
<select id="getByName" resultType="com.pika.entity.Balance">
select *
from t_balance
where name like ${name}
</select>
也就是说,无论 name 值是否存在, 由于 or 1 永远成立,会查询出数据库表中所有数据。
其它
lambda获取属性名,源码解析
最近项目中使用mybatisplus 作为项目的orm,效率比mybatis提升了不少,用起来相当方便,其中通过lambda表达式取得字段名,特别方便
LambdaQueryWrapper<SpClips> where = new LambdaQueryWrapper<>();
where.in(SpClips::getClipId, ids);
这是怎么实现的呢,带着好奇心我们来分析一下这块的源码。 首先我们看下 AbstractLambdaWrapper 抽象类
protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
return getColumn(LambdaUtils.resolve(column), onlyColumn);
}
columnToString 函数将lambda表达式转换成字段名, 我们再看下 LambdaUtils.resolve(column) 这行代码处理的逻辑
/**
* 解析 lambda 表达式, 该方法只是调用了 {@link SerializedLambda#resolve(SFunction)} 中的方法,在此基础上加了缓存。
* 该缓存可能会在任意不定的时间被清除
*
* @param func 需要解析的 lambda 对象
* @param <T> 类型,被调用的 Function 对象的目标类型
* @return 返回解析后的结果
* @see SerializedLambda#resolve(SFunction)
*/
public static <T> SerializedLambda resolve(SFunction<T, ?> func) {
Class<?> clazz = func.getClass();
return Optional.ofNullable(FUNC_CACHE.get(clazz))
.map(WeakReference::get)
.orElseGet(() -> {
SerializedLambda lambda = SerializedLambda.resolve(func);
FUNC_CACHE.put(clazz, new WeakReference<>(lambda));
return lambda;
});
}
该段代码将lambda表达式转换成 SerializedLambda 对象,我们在来看看SerializedLambda 有哪些信息
private Class<?> capturingClass;
private String functionalInterfaceClass;
private String functionalInterfaceMethodName;
private String functionalInterfaceMethodSignature;
private String implClass;
private String implMethodName;
private String implMethodSignature;
private int implMethodKind;
private String instantiatedMethodType;
private Object[] capturedArgs;
这里面有方法名信息,到目前为止,实现思路就比较清楚了,
初始化类:TableInfoHelper 核心类:LambdaUtils 、SerializedLambda
版权声明:如无特别声明,本站收集的文章归 HuaJi66/Others 所有。 如有侵权,请联系删除。
联系邮箱: GenshinTimeStamp@outlook.com
本文标题:《 MyBatis-Plus Zero 学习笔记 》