Capítulo 3MyBatis
3. Mapeamento de múltiplas tabelas MyBatis
2. Mapeamento um para um
2.1 Descrição dos requisitos
- Consulte o pedido com base no ID e nas informações do usuário associado ao pedido!
2.2 Interface do OrderMapper
public interface OrderMapper {
Order selectOrderWithCustomer(Integer orderId);
}
2.3 Arquivo de configuração OrderMapper.xml
<!-- 创建resultMap实现“对一”关联关系映射 -->
<!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上“ResultMap” -->
<!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
<resultMap id="selectOrderWithCustomerResultMap" type="order">
<!-- 先设置Order自身属性和字段的对应关系 -->
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
<!-- 使用association标签配置“对一”关联关系 -->
<!-- property属性:在Order类中对一的一端进行引用时使用的属性名 -->
<!-- javaType属性:一的一端类的全类名 -->
<association property="customer" javaType="customer">
<!-- 配置Customer类的属性和字段名之间的对应关系 -->
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
</association>
</resultMap>
<!-- Order selectOrderWithCustomer(Integer orderId); -->
<select id="selectOrderWithCustomer" resultMap="selectOrderWithCustomerResultMap">
SELECT order_id,order_name,c.customer_id,customer_name
FROM t_order o
LEFT JOIN t_customer c
ON o.customer_id=c.customer_id
WHERE o.order_id=#{orderId}
</select>
- Para o relacionamento correspondente, consulte a figura abaixo:
2.4 Arquivo mapeador de registro global Mybatis
<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
<mappers>
<!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
<mapper resource="mappers/OrderMapper.xml"/>
</mappers>
Programa de teste de 2,5 junho
@Slf4j
public class MyBatisTest {
private SqlSession session;
// junit会在每一个@Test方法前执行@BeforeEach方法
@BeforeEach
public void init() throws IOException {
session = new SqlSessionFactoryBuilder()
.build(
Resources.getResourceAsStream("mybatis-config.xml"))
.openSession();
}
@Test
public void testRelationshipToOne() {
OrderMapper orderMapper = session.getMapper(OrderMapper.class);
// 查询Order对象,检查是否同时查询了关联的Customer对象
Order order = orderMapper.selectOrderWithCustomer(2);
log.info("order = " + order);
}
// junit会在每一个@Test方法后执行@@AfterEach方法
@AfterEach
public void clear() {
session.commit();
session.close();
}
}
2.6 Palavras-chave
- Na associação "um para um", temos muitas configurações, mas as únicas palavras-chave são: associação e javaType
3. Mapeamento para muitos
3.1 Descrição dos requisitos
- Consulte clientes e informações de pedidos relacionados ao cliente!
3.2 Interface do CustomerMapper
public interface CustomerMapper {
Customer selectCustomerWithOrderList(Integer customerId);
}
3.3 Arquivo CustomerMapper.xml
<!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 -->
<resultMap id="selectCustomerWithOrderListResultMap"
type="customer">
<!-- 映射Customer本身的属性 -->
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
<!-- collection标签:映射“对多”的关联关系 -->
<!-- property属性:在Customer类中,关联“多”的一端的属性名 -->
<!-- ofType属性:集合属性中元素的类型 -->
<collection property="orderList" ofType="order">
<!-- 映射Order的属性 -->
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
</collection>
</resultMap>
<!-- Customer selectCustomerWithOrderList(Integer customerId); -->
<select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">
SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
FROM t_customer c
LEFT JOIN t_order o
ON c.customer_id=o.customer_id
WHERE c.customer_id=#{
customerId}
</select>
- Para o relacionamento correspondente, consulte a figura abaixo:
3.4 Arquivo mapeador de registro global Mybatis
<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
<mappers>
<!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
<mapper resource="mappers/OrderMapper.xml"/>
<mapper resource="mappers/CustomerMapper.xml"/>
</mappers>
Programa de teste de 3,5 junho
@Test
public void testRelationshipToMulti() {
CustomerMapper customerMapper = session.getMapper(CustomerMapper.class);
// 查询Customer对象同时将关联的Order集合查询出来
Customer customer = customerMapper.selectCustomerWithOrderList(1);
log.info("customer.getCustomerId() = " + customer.getCustomerId());
log.info("customer.getCustomerName() = " + customer.getCustomerName());
List<Order> orderList = customer.getOrderList();
for (Order order : orderList) {
log.info("order = " + order);
}
}
3.6 Palavras-chave
- No relacionamento “para-muitos”, também existem muitas configurações, mas as mais críticas são: “coleção” e “ofType”
4. Resumo do mapeamento multitabelas
4.1 Otimização de mapeamento multitabela
atributo de configuração | Atribuir significado | Valor opcional | valor padrão |
---|---|---|---|
autoMappingBehavior | Especifica como MyBatis deve mapear colunas automaticamente para campos ou propriedades. NONE significa desativar o mapeamento automático; PARTIAL mapeará automaticamente apenas os campos que não possuem mapeamentos de resultados aninhados definidos. FULL mapeia automaticamente qualquer conjunto de resultados complexo (aninhado ou não). | NENHUM, PARCIAL, COMPLETO | PARCIAL |
-
Podemos definir autoMappingBehavior como completo e, ao executar o mapeamento resultMap de várias tabelas, podemos omitir a tag de resultado que está em conformidade com as regras de mapeamento de nomenclatura de colunas e atributos (nome da coluna = nome do atributo ou ativar o mapeamento de caso de camelo ou personalizar o mapeamento) !
-
Modifique mybatis-sconfig.xml:
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
- Modifique teacherMapper.xml
<resultMap id="teacherMap" type="teacher">
<id property="tId" column="t_id" />
<!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!-- <result property="tName" column="t_name" />-->
<collection property="students" ofType="student" >
<id property="sId" column="s_id" />
<!-- <result property="sName" column="s_name" />-->
</collection>
</resultMap>
4.2 Resumo do mapeamento multitabelas
relação de conexão | Palavras-chave do item de configuração | Arquivo de configuração e local específico |
---|---|---|
Um a um | tag de associação/atributo javaType/atributo de propriedade | Dentro da tag resultMap no arquivo de configuração do Mapper |
para muitos | tag de coleção/atributo ofType/atributo de propriedade | Dentro da tag resultMap no arquivo de configuração do Mapper |
4. Declarações dinâmicas MyBatis
1. Requisitos e introdução da declaração dinâmica
- Muitas vezes encontramos muitas situações em que as consultas são feitas com base em muitas condições de consulta, como a procura de emprego no Zhaopin Recruitment. Muitas vezes há situações em que muitas condições não têm valor.Como devemos completar a instrução SQL final em segundo plano?
- SQL dinâmico é um dos recursos poderosos do MyBatis. Se utilizou JDBC ou outras estruturas semelhantes,deve compreender como é doloroso unir instruções SQL de acordo com diferentes condições.Por exemplo,certifique-se de não se esquecer de adicionar os espaços necessários ao unir,e tenha cuidado para remover a vírgula do último nome da coluna na lista. Usando SQL dinâmico, você pode se livrar completamente dessa dor.
- Usar SQL dinâmico não é fácil, mas MyBatis torna esse recurso significativamente mais fácil de usar com a poderosa linguagem SQL dinâmica que pode ser usada em qualquer instrução de mapa SQL.
- Se você já usou JSTL ou qualquer processador de texto baseado em uma linguagem semelhante a XML, os elementos SQL dinâmicos podem parecer familiares para você. Nas versões anteriores do MyBatis, demorava para entender um grande número de elementos. Com a ajuda de poderosas expressões baseadas em OGNL, MyBatis 3 substituiu a maioria dos elementos anteriores e simplificou bastante os tipos de elementos. Agora, os tipos de elementos a serem aprendidos são menos da metade dos originais.
2. tags se e onde
- O cenário mais comum para usar SQL dinâmico é incluir parte de uma cláusula where/if baseada em condições. por exemplo:
<!-- 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>
3. definir etiqueta
<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
update t_emp
<!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
<!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
<set>
<if test="empName != null">
emp_name=#{empName},
</if>
<if test="empSalary < 3000">
emp_salary=#{empSalary},
</if>
</set>
where emp_id=#{empId}
<!--
第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
第二种情况:部分条件满足 SET emp_salary=?
第三种情况:所有条件都不满足 update t_emp where emp_id=?
没有set子句的update语句会导致SQL语法错误
-->
</update>
4. etiqueta de corte (entenda)
-
Use a tag trim para controlar se ambas as extremidades da parte condicional contêm determinados caracteres
- atributo prefixo: especifica o prefixo a ser adicionado dinamicamente
- atributo suffix: especifica o sufixo a ser adicionado dinamicamente
- Atributo prefixOverrides: Especifique o prefixo a ser removido dinamicamente, use "|" para separar possíveis valores múltiplos
- Atributo suffixOverrides: Especifique o sufixo a ser removido dinamicamente, use "|" para separar possíveis valores múltiplos
<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_age,emp_salary,emp_gender
from t_emp
<!-- prefix属性指定要动态添加的前缀 -->
<!-- suffix属性指定要动态添加的后缀 -->
<!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
<!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
<!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null">
emp_name=#{empName} and
</if>
<if test="empSalary > 3000">
emp_salary>#{empSalary} and
</if>
<if test="empAge <= 20">
emp_age=#{empAge} or
</if>
<if test="empGender=='male'">
emp_gender=#{empGender}
</if>
</trim>
</select>
5. escolha / quando / caso contrário marque
-
Entre as múltiplas condições de ramificação, apenas uma é executada.
- Execute julgamentos condicionais sequencialmente de cima para baixo
- A primeira ramificação encontrada que satisfaça a condição será tomada
- Filiais seguintes à filial adotada não serão consideradas
- Se todas as ramificações quando não forem satisfeitas, execute a ramificação caso contrário
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary < 3000">emp_salary < 3000</when>
<otherwise>1=1</otherwise>
</choose>
<!--
第一种情况:第一个when满足条件 where emp_name=?
第二种情况:第二个when满足条件 where emp_salary < 3000
第三种情况:两个when都不满足 where 1=1 执行了otherwise
-->
</select>
6. etiqueta foreach
6.1 Uso básico
- Exemplo usando inserção em lote
<!--
collection属性:要遍历的集合
item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
index属性:这里起一个名字,便于后面引用
遍历List集合,这里能够得到List集合的索引值
遍历Map集合,这里能够得到Map集合的key
-->
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
<!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
(#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach>
6.2 Coisas a serem observadas ao atualizar em lote
- O exemplo acima de inserção em lote é essencialmente uma instrução SQL, enquanto a implementação da atualização em lote requer que várias instruções SQL sejam reunidas e separadas por ponto e vírgula. Ou seja, várias instruções SQL são enviadas ao mesmo tempo para execução do banco de dados. Neste momento, você precisa definir o endereço URL das informações de conexão do banco de dados:
alex.dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true
- A tag foreach correspondente é a seguinte:
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch">
<foreach collection="empList" item="emp" separator=";">
update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}
</foreach>
</update>
6.3 Sobre o atributo de coleção da tag foreach
- Se a anotação @Param não for usada para especificar um nome específico para o parâmetro do tipo Lista na interface, então coleção ou lista poderá ser usada por padrão no atributo de coleção para se referir à coleção de lista. Isso pode ser visto através das informações de exceção:
Parameter 'empList' not found. Available parameters are [arg0, collection, list]
- No desenvolvimento real, para evitar certos mal-entendidos causados por expressões obscuras, é recomendado usar a anotação @Param para declarar claramente o nome da variável e, em seguida, referenciar os parâmetros recebidos no atributo de coleção da tag foreach de acordo com o nome especificado pela anotação @Param.
7. fragmento SQL
7.1 Extraia fragmentos SQL duplicados
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>
7.2 Faça referência ao fragmento SQL extraído
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>