05 My Batis 复杂映射

1 动态SQL拼接

2 XML文件映射HelloWord

MyBatis提供了注解定义SQL与Mapper接口的映射关系,还提供了利用XML文件映射SQL与接口方法的关系。相对于注解方式,XML文件的更加灵活强大,可以实现复杂的动态SQL拼接,以及复杂结果的映射功能。

首先利用XML文件实现一个HelloWorld查询,体验以下基本的XML文件映射功能。在DemoMapper接口上定义一个test方法,此时方法上不用注解声明SQL:

package cn.tedu.mapper;

import org.apache.ibatis.annotations.Select;

public interface DemoMapper {
	
	@Select("Select 'Hello World!'")
	String hello();
	
	String test();
			
}

然后利用XML文件为接口提供SQL映射关系:新建文件resources/mappers/DemoMaper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.mapper.DemoMapper">
	<!-- id 属性映射到DemoMapper接口中的test方法 resultType 映射到test返回指定的类型 -->
	<select id="test" resultType="java.lang.String">
		SELECT 'Hello World'
	</select>
</mapper>

在sqlSessionFactory配置中加载DemoMapper.xml文件

	/*
	 *	配置SqlSessionFactory的作用是告诉MyBatis如何找到目标数据库 
	 */
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource,
			@Value("classpath:mappers/*.xml")Resource[] mapperLocations) 
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		//将所有的Mapper映射xml文件注入给MyBatis
		bean.setMapperLocations(mapperLocations);
		return bean.getObject();
	}

测试案例:

	@Test
	public void testTest() {
		DemoMapper mapper = ctx.getBean("demoMapper",DemoMapper.class);
		String str = mapper.test();
		System.out.println(str);
	}

配置sqlSessionFactory时候@Value("classpath:mappers/.xml“)的写法非常不好,因为将classpath:mappers/.xml固定写在代码中了,如果后期需要修改就很不方便,可以将这个配置信息移动到配置文件中,这样就更好了,代码中使用表达式读取配置文件信息

jdbc.properties:

db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/tedu_ums?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
db.username=root
db.password=root
db.maxActive=10
db.initialSize=2
mybatis.mapper.location=classpath:mappers/*.xml

代码中使用表达式读取配置信息:

	/*
	 *	配置SqlSessionFactory的作用是告诉MyBatis如何找到目标数据库 
	 */
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource,
			@Value("${mybatis.mapper.location}")Resource[] mapperLocations)  
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		//将所有的Mapper映射xml文件注入给MyBatis
		bean.setMapperLocations(mapperLocations);
		return bean.getObject();
	}

1.2 可变参数

关于可变参数:

  1. 每个方法中最多只能有1个可变参数,且必须是方法的最后一个参数;
  2. 调用这种方法时,可变参数的值的数量可以是0个或若干个;
  3. 在方法内部处理参数值时,可变参数就是一个数组。
	public static void sum(int... numbers) {
		int result = 0;
		for (int i = 0; i < numbers.length; i++) {
			result += numbers[i];
		}
		System.out.println(result);
	}

测试案例:

	@Test
	public void testSum() {
		sum();
		sum(1);
		sum(1,2);
		sum(1,2,3);
		sum(1,2,3,4);
	}

测试:
在这里插入图片描述

1.3 动态SQL–foreach

动态SQL:根据调用的方法时给出的参数不同,最终生成的SQL语句会不相同。

假设需要实现”删除id = ?,id = ?,id = ?的用户数据“,即“一次性删除若干条数据”,具体需要删除几条,哪几条,对于研发人员来说,都是不确定的。实现这样的操作,需要执行的SQL语句大概是:

DELETE FROM t_userWHERE id IN (?,?,?)

以上SQL语句中,问好的数量与值都是开发人员无法确定的,但是,可以肯定的是“这里的问号表示的都是id的值”。

实现这个功能,在设计抽象方法时,可以将若干个问号的值,也就是若干个id的值,使用集合或数组来表示,例如:

Integer deleteByIds(List<Integer> ids);

或:

Integer deleteByIds(Integer[] ids);

甚至,还可以将参数声明为可变参数,例如:

Integer deleteByIds(Integer... ids);

完成抽象方法的声明之后,就需要配置以上抽象方法的映射!配置映射时,需要使用节点实现动态SQL中的遍历,即遍历以上方法中的参数值,以生成?,?,?,这样的SQL语句片段。

例如可以配置为:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.mapper.UserMapper">
	<!-- Integer deleteByIds(Integer[] ids); -->
	<delete id = "deleteByIds">
		DELETE FROM t_user WHERE id IN (
			<foreach collection="array" item="id" separator=",">
				#{id}
			</foreach>
		)
	</delete>
</mapper>

可变数组也属性array类型

在以上的配置中,各属性的作用:
在这里插入图片描述

  • collection:表示被遍历的对象,当抽象方法的参数只有1个且没有添加@Param注解时,如果参数的类型是List集合类型的,则取值为list,如果参数的类型是数组类型的,则取值为array;否则,取值为@Param注解中配置的名称;
  • iteam:在遍历过程中,被遍历到的数据的名称,将根据这个名称来使用被遍历到的数据,所以,在的子级,将根据这个属性配置的值来使用数据;
  • separator:分隔符,用于分割遍历时生成的SQL语句片段的起始字符串和结束字符串,比如:open="(" close=")"

测试案例,在数据库中检查删除的结果:

	@Test
	public void testDeleteByIds() {
		UserMapper mapper = ctx.getBean("userMapper",UserMapper.class);
		Integer n = mapper.deleteByIds(18,19,20);
		System.out.println(n);
	}

1.4 显示MyBatis生成的SQL

MyBatis提供了显示动态生成的SQL功能,利用这个功能就可以查看SQL,分析问题原因,甚至是优化查询性能。

这个功能是利用日志系统实现的,所以使用这个功能需要先导入日志包:

		<!--日志API -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.25</version>
		</dependency>

在resource文件夹中增加日志配置 log4j.properties:

log4j.rootLogger=ERROR, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

# MyBatis Mapper
log4j.logger.cn.tedu.mapper.UserMapper=TRACE

其中最后一行的作用是开启cn.tedu.mapper包中全部Mapper接口的SQL日志输出,如果担心日志太多,可以制定具体Mapper接口,实现只输出一个Mapper接口的SQL日志:
在这里插入图片描述

有了上述配置,再次测试testDeleteByIds方法,就可以看到SQL执行情况了:
在这里插入图片描述

1.5 动态SQL – if

标签的配置语法格式是:

	<if test="表达式">
		SQL语句表达式
	</if>

使用可以根据判断条件不同而得到不同的SQL语句。

在使用时,如果有多个条件存在逻辑关系,可以使用“and”表示“与”,使用“or”表示“或”。

在MyBatis中的并没有对应的else标签,所以只能判断“符合条件时应该怎么执行”,并没有不符合条件时的做法。

如果一定要实现if … else的效果,需要使用系列标签,基本格式是:

	<choose>
		<when test="表达式">
			满足条件的SQL语句片段
		</when>
		<otherwise>
			不满足条件时的SQL语句片段	
		</otherwise>
	</choose>

比如讨论以下更新功能:

UPDATE t_user
		SET
			username=#{username},
			password=#{password},
			age=#{age},
			phone=#{phone},
			email=#{email}		
		WHERE id=#{id}

整个更新语句并不好,因为这个SQL无条件的更新了全部的属性,如果我只是更新了用户的密码一个属性,这个SQL也更新了全部的属性。所以更新用户密码的SQL最好是:

		UPDATE t_user
		SET
			password=#{password},
		WHERE id=#{id}

利用if标签就可以实现动态的SQL拼接:

	<update id="updateUserInfo" parameterType="cn.tedu.entity.User">
		UPDATE t_user
		SET
			<if test="username != null">
				username=#{username},
			</if>
			<if test="password != null">
				password=#{password},
			</if>
			<if test="age != null">
				age=#{age},
			</if>
			<if test="phone != null">
				phone=#{phone},
			</if>
			<if test="email != null">
				email=#{email}
			</if>
			
		WHERE id=#{id}
	</update>

在UserMapper中声明抽象方法:

Integer updateUserInfo(User user);

MyBatis的Mapper中不能定义重载方法,在前述案例中已经定义了updateUser,这里为了避免方法名相同,就定义了方法updateUserInfo

测试案例:

	@Test
	public void testUpdateUserInfo() {
		UserMapper mapper = ctx.getBean("userMapper",UserMapper.class);
		User user = new User();
		user.setId(3);
		user.setUsername("蜻蜓队长");
		user.setPassword("1111111111");
		Integer n = mapper.updateUserInfo(user);
		System.out.println(n);
	}

测试出现了错误,主要内容如下:
在这里插入图片描述

在这里插入图片描述

这个结果的SQL语句显然出现了错误,其中在WHERE之前多了一个“,”,究其原因是因为测试数据包含俩个属性,username和password,生成动态SQL时候生成的SQL语句password属性后部多了一个“,”,MyBatis考虑到这个情况提供了set标签消除多余的符号:

	<update id="updateUserInfo" parameterType="cn.tedu.entity.User">
		UPDATE t_user
		<set>
			<if test="username != null">
				username=#{username},
			</if>
			<if test="password != null">
				password=#{password},
			</if>
			<if test="age != null">
				age=#{age},
			</if>
			<if test="phone != null">
				phone=#{phone},
			</if>
			<if test="email != null">
				email=#{email}
			</if>
		</set>
		WHERE id=#{id}
	</update>

再次测试案例,可以看到控制台上的SQL正常了:

在这里插入图片描述

在这里插入图片描述

1.6 动态参数查询

在应用程序中经常有多参数查询现象,比如有如下功能:
在这里插入图片描述
此种功能其对应的SQL是如下情况:

  1. 如果一个条件都不给,执行的SQL为:
    • SELECT * FROM t_user
  2. 如果只有一个条件,执行查询一个条件的SQL,有3种情况
    • SELECT * FROM t_user WHERE username like ?
    • SELECT * FROM t_user WHERE age = ?
    • SELECT * FROM t_user WHERE phone like ?
  3. 如果俩个条件,就执行俩个条件的SQL,俩个条件是并且关系,有3种情况:
    • SELECT * FROM t_user WHERE username like ? AND age = ?
    • SELECT * FROM t_user WHERE username like ? AND phone like ?
    • SELECT * FROM t_user WHERE age like ? AND phone = ?
  4. 如果有3个条件,就执行3个条件的SQL,3个条件并且连接:
    • SELECT * FROM t_user WHERE username like ? AND age = ?AND phone like ?

上述的SQL显然非常复杂,但是MyBatis的if标签可以轻松解决这种动态SQL拼接问题:

首先定义抽象接口,在接口中定义3个参数:

	List<User> findUserByParams(
			@Param("username") String username,
			@Param("age") String age,
			@Param("phone") String phone
			);

在UserMapper.xml中映射SQL与抽象方法的对应关系

	<select id="findUserByParams" resultType="cn.tedu.entity.User">
		SELECT
			id,
			username,
			password,
			age,
			phone,
			email
		FROM
			t_user
		<where>
			<if test="username != null">
				username like #{username}
			</if>
			<if test="age != null">
				AND age like #{age}
			</if>
			<if test="phone != null">
				AND phone like #{phone}
			</if>
		</where>
	</select>

resultType="cn.tedu.entity.User"属性用于声明返回值List的元素类型。if标签用于动态生成查询条件,where标签用于自动处理多余的AND运算符。

测试时候,MyBatis会根据参数的不同自动拼接SQL,没有参数就没有where子句,三个参数就生成三个条件的子句。

俩个参数的测试案例:

	@Test
	public void testFindUserByParams() {
		UserMapper mapper = ctx.getBean("userMapper",UserMapper.class);
		List<User> list = mapper.findUserByParams("F%", 21, null);
		list.forEach(user -> System.out.println(user));
	}

2 关联查询

2.1 数据库关联查询

数据库中的表直接经常是有业务关联关系,比如用户隶属于所在的部门,也就是一个部门对应多个用户,这俩个表的关系如图所示:
在这里插入图片描述

利用SQL增加一个t_department表,并建立t_user表和t_department关联关系:

  • 创建t_department表
CREATE TABLE t_department (
id INT(11) AUTO_INCREMENT COMMENT '部门ID',
name VARCHAR(20) NOT NULL COMMENT '部门名',
PRIMARY KEY (id)
)DEFAULT CHARSET=utf8;
  • 在t_department表中插入数据
INSERT INTO t_department (name) VALUES ('Java'),('C++'),('Linux');
  • 在t_user表中增加外键列,department_id
ALTER TABLE t_user ADD COLUMN department_id INT;
  • 根据 t_department 中id更新t_user表中 department_id 列的值,供测试使用
UPDATE t_user u SET department_id = (SELECT d.id FROM t_department d WHERE name='Java') WHERE u.id >5;
UPDATE t_user u SET department_id = (SELECT d.id FROM t_department d WHERE name='C++') WHERE u.id <=5;
  • 建立外键引用关系
ALTER TABLE t_user ADD CONSTRAINT dept_user FOREIGN KEY (DEPARTMENT_ID) REFERENCES t_department(id);

利用上述SQL语句更新数据库,以便进行后续实验。

2.2 利用值对象返回查询结果

在软件界面展示数据时候往往不会展示数据表到全部列,比如展示用户信息时候展示如下结果:
在这里插入图片描述

这样查询结果的SQL语句应该为:

SELECT u.id, username, d.name 
FROM t_user u 
LEFT JOIN t_department d 
ON u.department_id = d.id;

此时查询结果只有3列,这种只有部分数据列的查询结果,可以利用值对象(Value Object)进行封装,查询结果中有几列,这个类型中就定义几个属性:

package cn.tedu.vo;

import java.io.Serializable;

public class UserVO implements Serializable{

	private Integer id;
	private String username;
	private String departmentname;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getDepartmentname() {
		return departmentname;
	}
	public void setDepartmentname(String departmentname) {
		this.departmentname = departmentname;
	}
	
	@Override
	public String toString() {
		return "UserVO [id=" + id + ", username=" + username + ", departmentname=" + departmentname + "]";
	}
	
}

在UserMapper中定义抽象的查询方法:

List<UserVO> findUserDepartment();

在UserMapper.xml中定义关联查询语句:

	<select id="findUserDepartment" resultType="cn.tedu.vo.UserVO">
		SELECT
			u.id,
			username,
			p.name as departmentName
		FROM
			t_user u
		LEFT JOIN
		t_department p
	ON 
		u.department_id = p.id
	</select>

如果查询结果列名与返回值对象UserVO属性名不一致时候,可以利用SQL列别名进行映射。将查询结果的列明与UserVO属性名一致。

测试:

	@Test
	public void testfindUserDepartment() {
		UserMapper mapper = ctx.getBean("userMapper",UserMapper.class);
		List<UserVO> list = mapper.findUserDepartment();
		list.forEach(u -> System.out.println(u));
	}

在这里插入图片描述

2.3 返回关联查询结果

假设需要实现:查询某个部门的信息,并且,需要显示该部门有哪些用户。

在这里插入图片描述

需要执行的SQL语句是:
在这里插入图片描述

在编写代码时,依然需要先设计抽象方法,而设计抽象方法之前,就需要先创建新的VO类,用于封装此次的查询结果。所以,先创建DepartmentVO类:

package cn.tedu.vo;

import java.io.Serializable;
import java.util.List;

import cn.tedu.entity.User;

public class DepartmentVO implements Serializable{
	
	private Integer id;
	private String name;
	private List<User> users;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public List<User> getUsers() {
		return users;
	}
	public void setUsers(List<User> users) {
		this.users = users;
	}
	@Override
	public String toString() {
		return "DepartmentVO [id=" + id + ", name=" + name + ", users=" + users + "]";
	}
	

}

此前使用的UserMapper.java都是配置处理用户数据的相关功能,此次,查询的主题是部门**的数据,抽象方法应该另外创建接口来存放,以便于管理数据。(可以放在同一个接口中,但是,不推荐这样做)
所以,应该先创建DepartmentMapper.java接口文件,然后,这个接口中添加抽象方法:

package cn.tedu.mapper;

import cn.tedu.vo.DepartmentVO;

public interface DepartmentMapper {

	DepartmentVO findVOById(Integer id);
	
}

完成接口中的抽象方法后,就应该开始配置SQL映射,此前的UserMapper.xml的根节点对应的是UserMapper接口,所以,将无法对应新创建的DepartmentMapper接口中的抽象方法,并且,出于规范管理代码的要求,也应该将管理部门数据的SQL语句写在新的XML文件中。

所以,创建新的UserMapper.xml并在其中配置SQL。

接下来的任务就是需要配置SQL映射,而此次的查询时,某个部门可能有多个用户,所以结果可能有多条,但是抽象方法的返回值是1个对象,那么,就存在MyBatis不知道如何将若干个查询结果封装到1个对象中去。所以,在配置时,就需要结合需要执行的SQL语句并使用来完成配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.tedu.mapper.DepartmentMapper">
	<resultMap type="cn.tedu.vo.DepartmentVO" id="deptMap">
		<result column="id" property="id"/>
		<result column="name" property="name"/>
		<collection property="users" ofType="cn.tedu.entity.User">
			<result column="userId" property="id"/>
			<result column="username" property="username"/>
			<result column="password" property="password"/>
			<result column="age" property="age"/>
			<result column="phone" property="phone"/>
			<result column="email" property="email"/>
		</collection>
	</resultMap>

	<select id="findVOById" resultMap="deptMap">
		SELECT
			d.id,
			name,
			u.id AS userId,
			username,
			password,
			age,
			phone,
			email
		FROM
			t_department d
		LEFT JOIN
			t_user u
		ON
			d.id = u.department_id
		WHERE
			d.id = #{id}
	</select>
</mapper>

在log4j.properties中追加语句,打开日志输出:

log4j.rootLogger=ERROR, stdout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

# MyBatis Mapper
log4j.logger.cn.tedu.mapper.UserMapper=TRACE
log4j.logger.cn.tedu.mapper.DepartmentMapper=TRACE

上述映射的工作原理是:
在这里插入图片描述

最后编写单元测试:

	@Test
	public void testFindVOById() {
		DepartmentMapper mapper = ctx.getBean("departmentMapper",DepartmentMapper.class);
		DepartmentVO vo = mapper.findVOById(1);
		System.out.println(vo);
	}

2.4 关于#{}和${}格式的占位符

在MyBatis中,配置SQL语句时,可以使用#{}或${}格式的占位符,用于表示参数的值。

SELECT * FROM ${tableName} WHERE username = #{username};

使用#{}格式的占位符时,只能表示某个参数值,不可以表示SQL语句中的某个片段。使用这种占位符时,MyBatis在处理过程中,是使用预编译做法,即:使用占位符来填充整个SQL语句,此时,并不关心占位符对应的值是多少,就直接将这样的值带进编译好的SQL语句中执行。

使用${}格式的占位符时,可以表示SQL语句中的任何部分,可以是某个值,也可以是SQL语句中的某个片段。使用这种占位符时,MyBatis在处理过程中,是先将占位符对应的值拼接到SQL语句中,然后,将整个SQL语句交给数据进行词法分析,语义分析,编译,如果没有问题,就可以直接执行SQL语句,如果出现语法错误,就会导致程序运行过程中出现异常。
在这里插入图片描述

使用#{}格式的占位符时,由于使用了预编译的做法,所以这种处理方式是安全的,而${}占位符是先拼接SQL语句,再执行过程,并没有预编译测处理,所以,存在SQL注入的风险。

所以,虽然使用${}的占位符可以实现的效果更多,但是,需要考虑数据类型的问题(例如字符串类型的值需要自行添加一对单引号),同时还存在SQL注入的风险,一般不推荐使用,应该优先使用#{}格式的占位符。

下面用一个安排说明以下${}和#{}的用法,实现一个通用的根据ID删除一行数据功能:

  • 根据ID删除一行用户数据的SQL语句:
DELETE FROM t_user WHERE id = ? 
  • 根据ID删除一个部门的SQL语句:
DELETE FROM t_department WHERE id = ? 

显然这俩个SQL非常类似,区别是SQL语句中表名不同,参数id不同,可以将SQL部分替换为${},将参数部门替换为#{},这样就得到:

DELETE FROM ${table} WHERE id = #{id}

这样就可以在DemoMapper中声明抽象方法:

	@Delete("DELETE FROM ${table} WHERE id = #{id}")
	Integer deleteById(		
			@Param("table") String table,
			@Param("id") Integer id) ;

测试案例:

	@Test
	public void testDeleteById() {
		DemoMapper mapper = ctx.getBean("demoMapper",DemoMapper.class);
		Integer n = mapper.deleteById("t_user", 10);
		System.out.println(n);
		n = mapper.deleteById("t_department", 11);
		System.out.println(n);
	}

注意:由于t_department 中的id与t_user的department_id 是外键参考关系,不能直接删除,否则会出现错误

3 总结MyBatis

3.1 MyBatis框架的主要作用:封装了JDBC,可以简化数据库编程;

3.2 在Spring 项目中使用MyBatis框架

  1. 需要添加MyBatis相关依赖;
  2. MyBatis框架的配置,包括jdbc.properties和MyBatisConfig.java中的配置;

3.3 数据库访问抽象方法的声明原则:

  1. 返回值:
    - 需要执行的是INSERT/DELETE/UPDATE类型的操作,使用integer作为返回值类型
    - 如果需要执行的是SELECT类型的操作,如果返回结果是一行数据,则可以使用实体类作为返回值类型
    - 如果是多表查询,应该先创建VO类,然后使用VO类作为返回值类型
    - 如果查询多行数据,使用Lisr<?>作为返回值类型
  2. 方法名称:自定义,但是不允许重载
  3. 参数列表:按需设计
    - 可以理解为SQL语句中哪些不确定的值,就可以设计出哪些参数,如果参数数据较多,可以将这些参数封装到1个对象中,特别是插入数据操作,必须使用封装的数据类型;
    - 如果参数的数量超过1个,必须使用@Param注解配置每一个参数的名称,该名称将用于配置SQL语句时的#{}的占位符中

3.4 为抽象方法映射SQL语句

  1. 简单的SQL可以使用注解直接映射
  2. 复杂动态SQL在XML文件中映射
    • 配置XML映射文件时,必须保留顶部的XML声明与文档类型声明语句;
    • 在配置XML映射文件时,根节点必须配置namespace属性,取值是对应的接口的全名
    • 按照SQL类型使用,,,节点,这些节点都必须指定id属性,取值是抽象方法的名称
    • 如果使用的是节点,必须配置resultType或resultMap中的某1个属性,如果查询多条数据,要声明List集合中的元素的类型即可
  3. 在SQL中可以使用${}HE #{}格式的占位符
    • ${}格式的占位符是SQL语句的某个片段,用于动态拼接SQL,存在SQL注入的风险,一般不推荐使用
    • #{}占位符代表SQL语句中的参数,是预编译的,所以没有SQL注入的风险
  4. 在处理查询时,MyBatis框架要求列明与属性名一致,可使用的解决方案有2种:
    • 在SQL语句中定义别名
    • 使用配置列明与属性名的对应
  5. 如果查询结果中存在1对多的对应关系,使用和映射查询结果;
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值