MyBatis从入门到多表关联

序号类型地址
1MySQLMySQL操作之概念、SQL约束(一)
2MySQLMySQL操作之数据定义语言(DDL)(二)
3MySQLMySQL操作之数据操作语言(DML)(三)
4MySQLMySQL操作之数据查询语言:(DQL)(四-1)(单表操作)
5MySQLMySQL操作之数据查询语言:(DQL)(四-2)(多表查询)
6MySQLMySQL操作之数据控制语言:(DC)(五)
7MySQLMySQL操作之数据库函数
8MySQLMySQL管理之数据类型
9MySQLMySQL管理之索引
10MySQLMySQL管理之事务管理
11MySQLMySQL管理之存储过程
12MySQLMySQL管理之视图
13MySQLMySQL管理之数据备份与还原
14MySQLLinux(centos 7.5)服务器安装MySQL
15MyBatisMyBatis从入门到多表关联
16MyBatisMyBatis常用方法
17MyBatisMybatis逆向工程的使用(附文件地址)
18MyBatisspring boot连接Mybatis数据库的配置文件(MySql、SQLserver、Oracle)
19MyBatis-PlusMybatis-Plus使用案例(包括初始化以及常用插件)
20MyBatis-Plusmybatis-plus代码生成器
21MyBatis-Plus自定义SQL
22MyBatis-PlusMybatis-Plus(连接Hive)
23MyBatis-PlusMyBatis-plus配置自定义SQL(执行用户传入SQL)
24MyBatis-PlusMybatis-Plus(Service CRUD 接口)


一、Spring JDBC

1、配置

Spring JDBC模块主要由4个包组成,分别是core(核心包)、dataSource(数据源包)、object(对象包)、support(支持包)。

包名描述
core包含了JDBC的核心功能,包裹JDBC Template类、SimpleJdbcInsert类、SimpleJdbcCall类以及NamedParameterJdbcTemplate类
dataSource访问数据源的实用工具类,有多种数据源的实现,可以在Java EE容器外部测试JDBC代码。
object以面向对象的方式访问数据库,它允许执行查询并将返回结果作为业务对象,可以在数据表的列和业务对象的属性之间映射查询结果
support包含了core和object包的支持类。

applicationContext.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
 	http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
  
	<!-- 1配置数据源 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<!--数据库驱动 -->
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<!--连接数据库的url -->
		<property name="url" value="jdbc:mysql://localhost:3306/spring" />
		<!--连接数据库的用户名 -->
		<property name="username" value="root" />
		<!--连接数据库的密码 -->
		<property name="password" value="root" />
	</bean>
  
	<!-- 2配置JDBC模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<!-- 默认必须使用数据源 -->
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!--定义id为accountDao的Bean-->
	<bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl">
		<!-- 将jdbcTemplate注入到accountDao实例中 -->
		<property name="jdbcTemplate" ref="jdbcTemplate" />
	</bean>
</beans>

  • dataSource:对数据源进行配置
  • jdbcTemplate:定义JdbcTemplate相关配置。
属性名称含义
driverClassName所使用的驱动名称,对应驱动JAR包中Driver类
url数据源所在地址
username访问数据库的用户名
password访问数据库的秘密

2、常用方法

  • Execute(String sql ):完成执行SQL语句功能。
  • update(String sql ):完成插入、更新和删除数据库的操作。
  • query(String sql):处理各种对数据库表的查询操作。

3、案例

AccountDao接口:

import java.util.List;

public interface AccountDao {

	// 通过id查询
	public Account findAccountById(int id);
	// 查询所有账户
	public List<Account> findAllAccount();
}

AccountDao实现类

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.util.List;
public class AccountDaoImpl implements AccountDao {
	// 声明JdbcTemplate属性及其setter方法
	private JdbcTemplate jdbcTemplate;
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	// 通过id查询账户数据信息
	@Override
	public Account findAccountById(int id) {
	    //定义SQL语句
	    String sql = "select * from account where id = ?";
	    // 创建一个新的BeanPropertyRowMapper对象
	    RowMapper<Account> rowMapper = 
	new BeanPropertyRowMapper<Account>(Account.class);
	    // 将id绑定到SQL语句中,并通过RowMapper返回一个Object类型的单行记录
	    return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
	}
  
	// 查询所有账户信息
	@Override
	public List<Account> findAllAccount() {
	    // 定义SQL语句
	    String sql = "select * from account";
	    // 创建一个新的BeanPropertyRowMapper对象
	    RowMapper<Account> rowMapper = 
	new BeanPropertyRowMapper<Account>(Account.class);
	    // 执行静态的SQL查询,并通过RowMapper返回结果
	    return this.jdbcTemplate.query(sql, rowMapper);
	}
}

测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;

public class JdbcTemplateTest {

    @Test
    public void findAccountByIdTest() {
        // 加载配置文件
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取AccountDao实例
        AccountDao accountDao =
                (AccountDao) applicationContext.getBean("accountDao");
        // 执行findAccountById()方法
        Account account = accountDao.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void findAllAccountTest() {
        // 加载配置文件
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取AccountDao实例
        AccountDao accountDao =
                (AccountDao) applicationContext.getBean("accountDao");
        // 执行findAllAccount()方法,获取Account对象的集合
        List<Account> account = accountDao.findAllAccount();
        // 循环输出集合中的对象
        for (Account act : account) {
            System.out.println(act);
        }
    }
}

二、Spring 事务管理

1、事务核心接口

在这里插入图片描述

1)PlatformTransactionManager

PlatformTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。

  • TransactionStatus getTransaction(TransactionDefinition var1) :用于获取事务状态信息。

  • void commit(TransactionStatus var1) throws TransactionException:用户提交事务。

  • void rollback(TransactionStatus var1) throws TransactionException:要黄光裕回滚事务。

有不同的实现类:

  • DataSourceTransactionManager:用于配置JDBC数据源的事务管理器,
  • HibernateTransactionManager:用于配置Hibernate的事务管理器。
  • JtaTransactionManager:用于配置全局事务管理器。

2)TransactionDefinition

事务定义(描述)的对象,定义了事务规则,并提供获取事务相关信息的方法。

  • String getName():获取事务对象名称。
  • int getIsolationLevel():获取事务的隔离级别。
  • int getPropagationBehavior():获取事务的传播行为。
  • int getTimeout():获取事务的超时时间。
  • boolean isReadOnly():获取事务是否只读。

3)TransactionDefinition

  • void flush():刷新事务。

  • boolean hasSavepoint():获取是否存在保存点。

  • boolean isCompleted():获取事务是否完成。

  • boolean isNewTransaction():获取是否是新事务。

  • void setRollbackOnly():获取是否回滚。

  • boolean isRollbackOnly():设置事务回滚。

2、事务管理方式

  • 编程式事务

通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。

  • 声明式事务(推荐使用)

通过AOP技术实现的事务管理,主要思想是将事务管理作为一个”切面“代码单独编写,然后通过AOP技术将事务管理的”切面“代码织入到业务目标类中。

可分为:基于XML方式和基于Annotation方式。

3、声明式事务管理(基于XML)

基于XML方式的声明式事务管理,是通过在配置文件中配置事务规则的相关生命来实现的。提供了tx命名空间来配置事务,其下提供了<tx:advice>元素来配置事务的通知(增强通知)。

<tx:advice>元素及其子元素:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IUdxeLa8-1627626118153)(/Users/mlamp/Desktop/2021-0718/00111.jpeg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5XkubQt3-1627626118154)(/Users/mlamp/Desktop/2021-0718/WechatIMG430.jpeg)]

<tx:method>元素的属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TeBwH09M-1627626118155)(/Users/mlamp/Desktop/2021-0718/33333.jpeg)]

案例:

AccountDao接口

public interface AccountDao {
	// 转账
	public void transfer(String outUser,String inUser,Double money);
}

AccountDao实现类:

	public void transfer(String outUser, String inUser, Double money) {
	    // 收款时,收款用户的余额=现有余额+所汇金额
	    this.jdbcTemplate.update("update account set balance = balance +? "
	            + "where username = ?",money, inUser);
	    // 模拟系统运行时的突发性问题
	    int i = 1/0;
	    // 汇款时,汇款用户的余额=现有余额-所汇金额
	    this.jdbcTemplate.update("update account set balance = balance-? "
	            + "where username = ?",money, outUser);
	}

测试类:

package com.lydms.jdbc;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import 
org.springframework.context.support.ClassPathXmlApplicationContext;
//测试类
public class TransactionTest {
	@Test
	public void xmlTest(){
		ApplicationContext applicationContext = 
		   new ClassPathXmlApplicationContext("applicationContext.xml");
		// 获取AccountDao实例
		AccountDao accountDao = 
            (AccountDao)applicationContext.getBean("accountDao");
		// 调用实例中的转账方法
		accountDao.transfer("Jack", "Rose", 100.0);
	    // 输出提示信息
	    System.out.println("转账成功!");
	}
}

事务配置类:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.3.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

<!-- 1.配置数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<!--数据库驱动 -->
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<!--连接数据库的url -->
		<property name="url" value="jdbc:mysql://localhost/spring" />
		<!--连接数据库的用户名 -->
		<property name="username" value="root" />
		<!--连接数据库的密码 -->
		<property name="password" value="root" />
   </bean>

<!-- 2.配置JDBC模板 -->
   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		 <!-- 默认必须使用数据源 -->
		 <property name="dataSource" ref="dataSource" />
   </bean>

<!--3.定义id为accountDao的Bean -->
   <bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl">
		 <!-- 将jdbcTemplate注入到AccountDao实例中 -->
		 <property name="jdbcTemplate" ref="jdbcTemplate" />
   </bean>	

<!-- 4.事务管理器,依赖于数据源 -->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
   </bean>	

<!-- 5.编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节 -->
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- name:*表示任意方法名称 -->
			<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
		</tx:attributes>
	</tx:advice>

<!-- 6.编写aop,让spring自动对目标生成代理,需要使用AspectJ的表达式 -->
	<aop:config>
		<!-- 切入点 -->
		<aop:pointcut expression="execution(* com.lydms.jdbc.*.*(..))" id="txPointCut" />
		<!-- 切面:将切入点与通知整合 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
	</aop:config>
</beans>

4、声明式事务管理(基于Annotation)(注解)

只需要做2件事:

  • 在Spring容器中注册事务注解驱动。
<!-- 5.注册事务管理器驱动 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
  • 在需要的位置上添加@Transactional注解

注解加在Bean类上:事务设置对整个Bean类的所有方法都起作用。
注解加在Bean类某个方法上:事务的设置只对该方法有效。

参数名称描述
value用于指定需要使用的事务管理器,默认为"",其别名为transactionManager。
transactionManager指定事务的限定符值,可用于确定目标事务管理器,匹配特定的限定值(或者Bean的name值),默认为”“,其别名为value。
Isolation用于指定事务的隔离级别。默认为Isolation.DEFAULT(底层事务的隔离级别)
noRollbackFor用于指定遇到特定异常时强制不回滚事务。
noRollbackForClassName用于指定遇到特定的多个异常时,强制不回滚事务。其属性值可以指定多个异常类名。
propagation用于指定事务的传播行为,默认为Propagation.REQUIRED。
read-only用于指定事务是否只读,默认为false。
rollbackFor用于指定遇到特定异常时强制回滚事务。
rollbackForClassName用于指定遇到特定的多个异常时强制回滚事务。其属性值可以指定多个异常类名。
timeout用于指定事务的超时时长,默认为TransactionDefinition.TIMEOUT_DEFAULT(即底层事务系统的默认时长)

案例:

注解配置:

<!-- 5.注册事务管理器驱动 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>

使用类

	@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
	public void transfer(String outUser, String inUser, Double money) {
	    // 收款时,收款用户的余额=现有余额+所汇金额
	    this.jdbcTemplate.update("update account set balance = balance +? where username = ?",money, inUser);
	    // 模拟系统运行时的突发性问题
	    int i = 1/0;
	    // 汇款时,汇款用户的余额=现有余额-所汇金额
	    this.jdbcTemplate.update("update account set balance = balance-? where username = ?",money, outUser);
	}

三、Mybatis案例

1、Mybatis工作原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nsMNtff1-1627626118156)(/Users/mlamp/Desktop/2021020513340640.png)]

2、Mybatis初始结构

在这里插入图片描述

3、初始案例

mybatis-config.xml

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<!--1.配置环境 ,默认的环境id为mysql-->
    <environments default="mysql">
        <!--1.2.配置id为mysql的数据库环境 -->
        <environment id="mysql">
            <!-- 使用JDBC的事务管理 -->
            <transactionManager type="JDBC" />
            <!--数据库连接池 -->
            <dataSource type="POOLED">
			  <property name="driver" value="com.mysql.jdbc.Driver" />
			  <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
			  <property name="username" value="root" />
			  <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    
<!--2.配置Mapper的位置 -->
    <mappers>
		<mapper resource="com/itheima/mapper/CustomerMapper.xml" />
    </mappers>
</configuration>

实体类(Customer.java)

public class Customer {
    private Integer id;
    private String username;
    private String jobs;
    private String phone;
}

Mapper映射(CustomerMapper.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">
<!-- namespace表示命名空间 -->
<mapper namespace="com.lydms.mapper.CustomerMapper">
    <!--根据客户编号获取客户信息 -->
	<select id="findCustomerById" parameterType="Integer"
		resultType="com.lydms.po.Customer">
		select * from t_customer where id = #{id}
	</select>
	
	<!--根据客户名模糊查询客户信息列表-->
	<select id="findCustomerByName" parameterType="String" resultType="com.lydms.po.Customer">
	    <!-- select * from t_customer where username like '%${value}%' -->
	    select * from t_customer where username like concat('%',#{value},'%')
	</select>
	
	<!-- 添加客户信息 -->
	<insert id="addCustomer" parameterType="com.lydms.po.Customer">
	    insert into t_customer(username,jobs,phone)
	    values(#{username},#{jobs},#{phone})
	</insert>
	
	<!-- 更新客户信息 -->
	<update id="updateCustomer" parameterType="com.lydms.po.Customer">
	    update t_customer set
	    username=#{username},jobs=#{jobs},phone=#{phone}
	    where id=#{id}
	</update>
	
	<!-- 删除客户信息 -->
	<delete id="deleteCustomer" parameterType="Integer">
	    delete from t_customer where id=#{id}
	</delete>
</mapper>

测试类:

package com.itheima.test;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.itheima.po.Customer;
/**
 * ���ų��������
 */
public class MybatisTest {
	
  /**
	 * 删除客户
	 */
	@Test
	public void deleteCustomerTest() throws Exception{		
	    // 1、读取配置文件
	    String resource = "mybatis-config.xml";
	    InputStream inputStream = Resources.getResourceAsStream(resource);
	    // 2、根据配置文件构建 SqlSessionFactory
	    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	    // 3、通过SqlSessionFactory 创建 SqlSession
	    SqlSession sqlSession = sqlSessionFactory.openSession();
	    // 4、SqlSession执行删除操作
	    // 4.1 执行SqlSession的删除方法,返回的是SQL语句影响的行数
	    int rows = sqlSession.delete("com.lydms.mapper.CustomerMapper.deleteCustomer", 4);
	    // 4.2 通过返回结果判断删除操作是否执行成功
	    if(rows > 0){
	        System.out.println("您成功删除了"+rows+"条数据");
	    }else{
	        System.out.println("执行删除操作失败");
	    }
	    // 4.3提交事务
	    sqlSession.commit();
	    // 5、关闭资源
	    sqlSession.close();
	}
  
  /**
	 * 根据客户编号查询客户信息
	 */
	@Test
	public void findCustomerByIdTest() throws Exception {
		// 1、读取配置文件
		String resource = "mybatis-config.xml";
		InputStream inputStream = 
                     Resources.getResourceAsStream(resource);
		// 2、根据配置文件构建 SqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		// 3、通过SqlSessionFactory 创建 SqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		// 4.SqlSession执行
		Customer customer = sqlSession.selectOne("com.itheima.mapper"
				  + ".CustomerMapper.findCustomerById", 1);
		// 
		System.out.println(customer.toString());
		// 5、关闭资源
		sqlSession.close();
	}
	
	/**
	 * 根据用户名称来模糊查询用户信息列表
	 */
	@Test
	public void findCustomerByNameTest() throws Exception{	
	    // 1、读取配置文件
	    String resource = "mybatis-config.xml";
	    InputStream inputStream = Resources.getResourceAsStream(resource);
	    // 2、根据配置文件构建 SqlSessionFactory
	    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	    // 3、通过SqlSessionFactory 创建 SqlSession
	    SqlSession sqlSession = sqlSessionFactory.openSession();
	    // 4、SqlSession执行映射文件汇总定义的SQL,并返回映射结果
	    List<Customer> customers = sqlSession.selectList("com.itheima.mapper"
					+ ".CustomerMapper.findCustomerByName", "j");
	    for (Customer customer : customers) {
	        //打印输出结果集
	        System.out.println(customer);
	    }
	    // 5、关闭资源
	    sqlSession.close();
	}
	
	/**
	 * 添加客户
	 */
	@Test
	public void addCustomerTest() throws Exception{		
	    // 1、读取配置文件
	    String resource = "mybatis-config.xml";
	    InputStream inputStream = Resources.getResourceAsStream(resource);
	    // 2、根据配置文件构建 SqlSessionFactory
	    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	    // 3、通过SqlSessionFactory 创建 SqlSession
	    SqlSession sqlSession = sqlSessionFactory.openSession();
	    // 4、SqlSession执行添加操作
	    // 4.1创建Customer对象,并向对象中添加数据
	    Customer customer = new Customer();
	    customer.setUsername("rose");
	    customer.setJobs("student");
	    customer.setPhone("13333533092");
	    // 4.2 执行SqlSession的插入方法,返回SQL影响的行数
		int rows = sqlSession.insert("com.itheima.mapper"
					+ ".CustomerMapper.addCustomer", customer);
	    // 4.3 通过返回结果判断插入操作是否执行成功
	    if(rows > 0){
	        System.out.println("您成功插入了"+rows+"条数据);
	    }else{
	        System.out.println("执行插入操作失败");
	    }
	    // 4. 提交事务
	    sqlSession.commit();
	    // 5、关闭资源
	    sqlSession.close();
	}

	/**
	 * 更新客户
	 */
	@Test
	public void updateCustomerTest() throws Exception{		
	    // 1、读取配置文件
	    String resource = "mybatis-config.xml";
	    InputStream inputStream = Resources.getResourceAsStream(resource);
	    // 2、根据配置文件构建 SqlSessionFactory
	    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	    // 3、通过SqlSessionFactory 创建 SqlSession
	    SqlSession sqlSession = sqlSessionFactory.openSession();
	    // sqlSession执行更新操作
	    // 4.1 创建Customer对象,对对象中的数据进行模拟更新
	    Customer customer = new Customer();
	    customer.setId(4);
	    customer.setUsername("rose");
	    customer.setJobs("programmer");
	    customer.setPhone("13311111111");
	    // 4.2 执行Sqlsession的更新方法,返回的是SQL语句影响的行数
	    int rows = sqlSession.update("com.itheima.mapper"
	            + ".CustomerMapper.updateCustomer", customer);
	    // 4.3 通过返回结果判断更新操作是否执行成功
	    if(rows > 0){
	        System.out.println("您成功更新了"+rows+"条数据");
	    }else{
	        System.out.println("执行更新操作失败");
	    }
	    // 4.4 提交事务
	    sqlSession.commit();
	    // 5、关闭资源
	    sqlSession.close();
	}
}

四、Mybatis核心配置

1、核心对象

1)SqlSessionFactory

  • SqlSessionFactory对象是线程安全的,它一旦被创建,在整个应用执行期间都会存在。

  • 如果多次创建同一个数据库的SqlSessionFactory,那么数据库资源将很容易被耗尽。

  • 通常每一个数据库只会对应一个SqlSessionFactory。

2)SqlSession

  • SqlSession对象包含了数据库中所有执行SQL操作的方法,由于其底层封装了JDBC连接,可以直接使用其实例来执行已映射的SQL语句。

  • 每个线程都应该有一个自己的SqlSession实例,并且该实例是不能被共享的。

  • SqlSession实例是线程不安全的。

  • 使用完SqlSession对象后,要及时关闭它。
    在这里插入图片描述

2、配置文件

在这里插入图片描述

1) <properties>

<properties>是一个配置属性的元素,该元素通常用于将内部的配置外在化,即通过外部的配置来动态的替换内部定义的属性。(数据库连接信息,通过db.properties进行配置)

(1)新建db.properties配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

(2)在Mybatis配置文件Mybatis-config.xml中添加配置信息

配置数据源信息

<properties resource=db.properties>

在Mybatis配置文件Mybatis-config.xml中添加<properties>配置信息

<dataSource type="POOLED">
  	<!-- 数据库驱动 -->
  <propertry name="driver" value="${jdbc.driver}"/>
  	<!-- 连接数据的url -->
  <propertry name="url" value="${jdbc.url}"/>
    <!-- 连接数据库的用户名 -->
  <propertry name="username" value="${jdbc.username}"/>
    <!-- 连接数据库的密码 -->
  <propertry name="password" value="${jdbc.password}"/>
<dataSource/>

完成以后,dataSource中连接数据库的4个属性(driver、url、username、password)值,会由db.properties文件对应的值来进行动态的替换。

2)<settings>

<settings>元素主要用于改变MyBatis运行时的行为。例如开启二级缓存,开启延迟加载等。

详细配置信息:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
具体使用

<settings>
    <!--  开启驼峰匹配:-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

开启延迟加载:

<settings>
    <!--  开启延迟加载的开关:-->
    <setting name="lazyLoadingEnabled" value="true"/>
      <!--  将积极加载改为延迟加载,即按需加载:-->
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

3)<typeAliases>

<typeAliases>元素用于为配置文件中的Java类型设置一个简短的名字,即设置别名。其中,alias为自定义别名。如果圣罗alias属性,Mybatis会默认将类名首字母小写后的名称作为别名。

自定义单个别名:

    <!-- 配置别名 -->
    <typeAliases>
        <!-- typeAlias:用来配置别名,方便映射文件使用,type:类的全限定类名,alias:别名 -->
        <typeAlias type="cn.lydms.pojo.User" alias="User"/>
    </typeAliases>

使用自动扫描包来定义别名

    <typeAliases>
      <!-- 使用自动扫描包来定义别名 -->
        <package name="com.lydms.pojo"/>
    </typeAliases>

代码中使用注解,则以代码中的为准(@Alias( ))

@Alias(value = "user")
public class User {
    
    private String name;
    private String address;
}

4) <typeHandler>

Mybatis在预处理语句中设置一个参数或者从结果集(ResultSet)中取出一个值时,都会用其内部注册了的typeHandler(类型处理器)进行相关处理。

<typeHandler>作用是将预处理语句中传入的参数从javaType(Java类型)转为jdbcType(JDBC类型),或者从数据库汇总取出结果时将jdbcType转为javaType。

    <typeHandlers>
        <!-- 1.注册一个类的类型处理 -->
        <typeHandler handler="com.lydms.domain.testFile"/>
        <!-- 2.注册一个包中所有类的类型处理 -->
        <typeHandler handler="com.lydms.domain"/>
    </typeHandlers>

常用的类型转换器:

类型处理器Java 类型JDBC 类型
BooleanTypeHandlerjava.lang.Boolean, boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte, byte数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandlerjava.lang.Short, short数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandlerjava.lang.Integer, int数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandlerjava.lang.Long, long数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandlerjava.lang.Float, float数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandlerjava.lang.Double, double数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME

5) <objectFactory>

Mybatis框架每次创建结果对象的新实例时,都会使用一个对象工厂(objectFactory)实例来完成。Mybatis中默认的<objectFactory的作用就是实例化目标类,可以通过默认方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。

6)<plugins>元素

Mybatis允许在已映射语句执行过程汇总的某一点进行拦截调用,这种拦截调用是通过插件来实现的。<plugins>元素的作用就是配置用户所开发的插件。

如果用户想要进行插件开发,必须要先了解其内部运行原理,因此在视图修改或重写已有方法的行为时,很可能会破坏Mybatis原有的核心。

7) <environments>

在配置文件中,<environments>元素用于对环境进行配置。Mybatis的环境配置实际上就是数据源的配置,可以通过<environments>元素配置多种数据源,即配置多种数据库。

案例:

    <!-- 1. 配置环境:可以配置多个环境,default:配置某一个环境的唯一标识,表示默认使用哪个环境 -->
    <environments default="development">
        <!-- 1.1. 配置环境,id:环境的唯一标识 -->
        <environment id="development">
            <!-- 1.1.1. 事务管理器,type:使用jdbc的事务管理器 -->
            <transactionManager type="JDBC"/>
            <!-- 1.1.2 数据源,type:池类型的数据源 -->
            <dataSource type="POOLED">
                <!-- 1.1.2.1 配置连接信息 -->
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
  • </environments>元素是环境配置的根元素,包含一个default属性,用于指定默认的环境。
  • </environment> </environments>元素的子元素,可以定制多个,其id属性用于表示所定义的环境的ID值,包含事务管理和数据源配置信息。
  • <transactionManager />元素用于配置事务管理,type属性用于事务管理方式(使用哪种事务)
  • </dataSource>元素用于配置数据源,type属性用于指定使用哪种数据源。

transactionManager:

可以配置两种类型事务管理器,JDBC和MANAGED。

**JDBC:**此配置直接使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来处理事务的作用域。

**MANAGED:**此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认关闭。

dataSource:

Mybatis框架提供了三个数据源类型:UNPOOLED、POOLED和JNDI。

UNPOOLED:

​ 配置此数据源类型后,在每次被请求时会打开和关闭连接。对没有性能要求的简单应用是一个很好的选择。

需要配置5中属性。

属性解析
driverJDBC驱动
url数据库的url地址
username登录数据库的用户名
password登录数据库的密码
defaultTransactionSolationLevel默认的连接事务隔离级别

POOLED:

此数据源利用“池”的概念将JDBC连接对象组织起来,避免了再创建新的连接实例时所需要初始化和认证的时间。这种方式使得并发web应用可以快速的响应请求,是当前流行的处理方式。

除了上面的5个配置以外,还有其它可额外配置的属性。

属性说明
poolMaximumActiveConnections在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
poolMaximumIdleConnections在任意时间可能存在的空闲连接数
poolMaximumCheckoutTime在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000毫秒,即20秒。
poolTimeToWait如果获取连接花费的时间较长,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直处于无提示的失败),默认值:20000毫秒,即20秒。
poolPingQuery发送到数据库的侦测查询,用于检验连接是否处在正常工作秩序中。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一定的错误消息。
poolPingEnabled是否启用侦测查询。若开启,必须使用一个可执行的SQL语句设置poolPingQuery属性(最好是一个非常快的SQL),默认值:false。
poolPingConnectionsNotUsedFor配置poolPingQuery的使用频度。可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(表示所有连接每一时刻都被侦测,只有poolPingEnabled的属性值为true时适用)

JNDI:

此数据源可以在EJB或应用服务器等容器中使用。容器可以集中或外部配置数据源,然后放置一个JNDI上下文引用。

配置JNDI数据源时,只需要配置两个属性。

属性说明
initial_context此属性主要用于在initialContext中寻找上下文,次属性为可选属性,在忽略时,data_source属性会直接从initialContext中寻找。
data_source此属性表示引用数据源实例位置的上下文的路径。如果提供了initial_context配置,那么程序会在其返回的上下文中进行查找;如果没有提供,则直接在initialContext中查找。

8) <mapper>元素

<mapper>元素用于指定Mybatis映射文件的位置。有4种方法引入映射器文件。

1、使用类路径引入

<mappers>
  	<mapper resource="com/lydms/mapper/UserMapper.xml">
</mappers>

2、使用本地文件路径引入

<mappers>
  	<mapper url="file://D://com/lydms/mapper/UserMapper.xml">
</mappers>

3、使用接口类引入

<mappers>
  	<mapper class="com.lydms.mapper.UserMapper">
</mappers>

4、使用包名引入

<mappers>
  	<package name="com.lydms.mapper">
</mappers>

3、映射文件

映射文件是Mybatis框架中十分重要的文件。

1)主要元素

在映射文件中,mapper:元素是映射文件的根元素,其它元素都是它的子元素。

在这里插入图片描述

2)select元素

<select>元素用于映射查询语句,可以帮助我们从数据库汇中读取出数据,并且组装数据给业务开发人员。

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

常用属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。

3)insert元素

<insert>元素用于映射插入语句,在执行完元素中定义的SQL语句后,会返回一个表示插入记录的整数。

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<insert>元素的属性和<select>元素的属性大部分相同,有3个特有的属性。

特有属性:

属性描述
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。

执行插入操作以后,很多时候我们会需要返回插入成功的数据生成的主键。

自动生成主键:

如果你的数据库支持自动生成主键的字段(比如 MySQL),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性(id),就可以实现。

<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
  insert into author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

不能自动生成主键:

数据库不支持主键自动增长(Oracle),或者支持增长的数据库取消了主键自动增长时,使用另一种方式。

<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="Integer" order="BEFORE">
    select if(max(id)is null,1,max(id)+1)as newId from author
  </selectKey>
  insert into author
    (id, username, password, email,bio)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio})
</insert>

在执行上述代码时,<selectKey>元素会首先运行,会通过自定义的语句来设置数据表中的主键(如果表中没有记录,则设置为1,否则就将id值+1,来作为新的主键),然后再调用插入语句。

<selectKey>元素在使用时可以设置以下几种属性。

<selectKey
  keyProperty="id"
  resultType="int"
  order="BEFORE"
  statementType="PREPARED">

selectKey 元素的属性:

属性描述
keyPropertyselectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。
resultType结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。
order可以设置为 BEFOREAFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType和前面一样,MyBatis 支持 STATEMENTPREPAREDCALLABLE 类型的映射语句,分别代表 Statement, PreparedStatementCallableStatement 类型。

4)update和delete元素

<update><delete>元素的使用比较简单,它们的属性配置也基本相同。

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<update><delete>元素的属性基本与<select>元素中的属性一致。与<insert>元素一样。<update><delete>元素在执行以后,会返回一表示影响记录条数的整数。

<update id="updateAuthor">
  update Author 
  set username = #{username}, password = #{password}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>

5)sql元素

<sql>元素的作用就是定义可重用的SQL代码片段,然后在其它语句中引用这一代码片段。

  • <sql id="customer">id,username,age<sql/>表示自定义SQL片段。
  • <include refid="customer">,表示引用SQL片段。

定义包含id、username、age的语句

<sql id="customer">id,username,age<sql/>

使用:

<select id="selectUsers" resultType="map">
  select
    <include refid="customer">
  from some_table
</select>

案例:

<!-- 定义表前缀 -->
<sql id="tablename">
  ${prefix}customer
</sql>

<!-- 定义要查询的表 -->
<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<!-- 定义查询的列 -->
<sql id="customerColumn">
  id,username,phone
</sql>

<!-- 定义要查询的表 -->
<select id="select" resultType="map">
  select
  <include refid="customerColumn">
 
  <include refid="someinclude">
    <property name="prefix" value="t_"/>
    <property name="include_target" value="tablename"/>
  </include>
    where id = #{id}
</select>

其中:

  • property定义了2个参数的值,将值赋值到相应语句中。

执行结果:

select id,username,phone from t_customer where id = ?

在这里插入图片描述

6)resultMap元素

<resultMap>元素表示结果映射集,是用来定义映射规则、级联的更新以及定义类型转换器等。

元素结构:

<!-- resultMap的元素结构-->
<resultMap id="" type="">
  <constructor>  <!-- 类在实例化时,用来注入结果到构造方法中-->
      <idArg/>  <!-- ID参数;标记结果作为ID -->
      <arg/>    <!-- 注入到构造方法的一个普通结果 -->
  <constructor/>
   
  <id />    <!-- 用于表示哪个列是主键 -->
  <result />    <!-- 注入到字段或JavaBean属性的普通结果 -->
  <association property="" />   <!-- 用于一对一关联 -->
  <collection property="coAuthor" />   <!-- 用于一对多关联 -->
  <discriminator javaType="" >   <!-- 使用结果值来决定使用哪个结果映射 -->
    <case value="" />   <!-- 基于某些值的结果映射 -->
  <discriminator/>
</resultMap>
  • <resultType>元素的type属性表示需要映射的POJO,id属性是这个resultType的唯一标识,它的子元素<constructor/>用于配置构造方法(当一个POJO中未定义无参的构造时,就可以使用<constructor/>元素进行配置)。
  • 子元素<id />用来表示哪个列是主键。
  • <result />用来表示POJO和数据表汇总普通列的映射关系。
  • <association/><collection/>用于处理多表时的关联关系。
  • <discriminator/>元素主要用于处理一个单独的数据库查询,返回很多不同数据类型结果集的情况。

案例:

<resultMap id="resultMap" type="com.lydms.mapper.UserMapper">
  <id property="id" column="t_id"/>
  <result property="name" column="t_name"/>
  <result property="age" column="t_age"/>
</resultMap>

<select id="findAll" resultMap="resultMap">
  SELECT * FROM t_user
</select>
  • 元素<id/><result/>的property属性表示User属性名,column属性表示数据表列名。
  • </select>元素的resultMap属性表示引用上面定义的resultMap。

五、动态SQL

1、简介

动态SQL是MyBatis的强大特征之一,MyBatis 3采用了功能强大的基于OGNL的表达式来完成动态SQL。

MyBatis中动态SQL元素:

元素说明
<if>判断语句、用于单条件分支判断
<choose>(<when><otherwise>)相当于Java中的switch…case…default语句,用于多条件分支判断
<where><trim><set>辅助元素,用于处理一些SQL拼接、特殊字符问题
<foreach>循环语句,常用于in语句等列举条件中
<bind>从OGNL表达式中创建一个变量,并将其绑定到上下文,常用于模糊查询的sql中

2、<if>元素

在MyBatis中<if>元素是最常用的判断语句,它类似于Java中的if语句,主要用于实现某些简单的条件选择。

案例:

<select id="findCustomerByName" parameterType="com.lydms.po.Customer" resultType="com.lydms.po.Customer">
    select * from t_customer where 1=1
    <if test="username! = null and username ! =''">
        and username like concat('%',#{username},'%')
    </if>
    <if test="jobds ! = null and jobs ! =''">
        and jobs=#{jobs}
    </if>
</select>
  • 使用<if>元素的test属性,分别对username和jobs进行了非空判断。
  • 只要test属性值为true,就会执行里面的条件语句。

User实体类

public class CompanyCustomerVo{
  private String username;
  private String jobs;
}

3、<choose><when><otherwise>元素

元素含义:

  • <choose>:表示执行当前选择逻辑

  • <when>:当满足条件执行内部SQL

  • <otherwise>:当上面条件都不满足,则执行内部SQL

1) 应用场景

  • 客户名称不为空,则以客户名称为查询条件。
  • 客户名称为空,客户职业不为空,则以客户职业为查询条件。
  • 客户名称为空,客户职业为空。则查询出电话不为空用户

2)Customer.xml

<select id="findCustomerByName" parameterType="com.lydms.po.Customer" resultType="com.lydms.po.Customer">
    select * from t_customer where 1=1
    <choose>
        <when test="username !=null and username !=''">
            and username like concat('%',#{username},'%')
        </when>
        <when test="jobs !=null and jobs !=''">
            and jobs = #{jobs}
        </when>
        <otherwise>
            and phone is not null
        </otherwise>
    </choose>
</select>
  • 使用<choose>元素进行SQL拼接,当第一个<when>元素中的条件为真,则只动态组装第一个<when>元素内的SQL片段,否则就继续向下判断第二个<when>元素中的条件是否为真。当前面所有的when元素中的条件都不为真时,则只组装<otherwise>元素内的SQL片段。
  • 第一个<when>条件满足,则执行第一个。不满足则执行第二个<when>。如果都不满足则执行<otherwise>

4、<where><trim>元素

<select id="findCustomerByName" parameterType="com.lydms.po.Customer" resultType="com.lydms.po.Customer">
    select * from t_customer where 1=1
    <if test="username! = null and username ! =''">
        and username like concat('%',#{username},'%')
    </if>
</select>

前面的语句中,都带了where 1=1,如果去掉以后,当条件满足,拼接出来的SQL为

select * from t_customer where and username like concat('%',?,'%');

其中where and拼接在一起,会造成语句报错。假如where 1=1,则保证不会出现这种情况。

1) <where>进行替换

<select id="findCustomerByName" parameterType="com.lydms.po.Customer" resultType="com.lydms.po.Customer">
    select * from t_customer
    <where>
        <if test="username! = null and username ! =''">
            and username like concat('%',#{username},'%')
        </if>
    </where>
</select>
  • 使用<where>元素对where 1=1 进行替换,<where>元素会自动判断组合条件下拼装的SQL语句,只有<where>元素内的条件成立时,才会在拼接SQL中加入where关键字。
  • 即使where后面内容有多余ANDOR<where>元素也会自动将它们去除。

2) <trim>进行替换

<select id="findCustomerByName" parameterType="com.lydms.po.Customer" resultType="com.lydms.po.Customer">
    select * from t_customer
    <trim prefix="where" prefixOverrides="and">
        <if test="username! = null and username ! =''">
            and username like concat('%',#{username},'%')
        </if>
    </trim>
</select>

同样使用<trime>元素对where 1=1条件进行了替换,<trime>元素的作用是去除一些特殊的字符串。

  • prefix属性表示语句的前缀;prefixOverrides元素表示需要去除的那些特殊字符串。

5、<set>元素

<set>元素主要用于更新操作,其主要作用是在动态包含的SQL语句前输出一个SET关键字,并将SQL语句中最后一个多余的逗号去除。

<update id="updateCustomer" parameterType="com.lydms.po.customer">
    update t_customer
    <set>
        <if test="username! = null and username ! =''">
            username=#{username},
        </if>
        <if test="jobds ! = null and jobs ! =''">
            jobs=#{jobs},
        </if>
    </set>
    where id =#{id}
</update>
  • 使用了<set><if>元素相结合的方式来组装update语句。
  • 其中<set>元素会动态前置SET关键字,同时也会消除SQL语句中最后一个多余的逗号。
  • 如果<set>中包含的内容都为空,则会报错,所以在使用<set>元素进行字段信息更新的时候,要确保出入的更新字段不能为空。

6、<foreach>元素

<foreach>元素,可以用来进行数组和集合循环遍历。通常咋构建IN条件时使用。

<select id="findCustomerByIds" parameterType="list" resultType="com.lydms.po.Cusotmer">
    select * from t_customer where id in
    <foreach item="id" index="index" collection="list"
             open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

几种属性解析:

  • Item:配置的是循环中元素(也就是循环内接收的元素)。
  • index:配置的是当前元素在集合的位置下标。
  • collection:配置的是list传递过来的参数类型(首字母小写),可以是一个array、list(或者collection)、map集合的键、POJO包装类中数组或者集合类型的属性名。
  • open和close:以什么元素将集合元素包装起来。(也就是IN的开头和结尾)
  • separator:配置各个元素之间的间隔符。

重点:

可以将任何迭代对象(集合、列表)和任何字段或对象数组传递给<foreach>作为集合参数。

当使用可迭代对象时:index是当前迭代的次数,item值是本次迭代获取的元素。

当使用字段(或Map.Entry对象的集合时):index是键,item是值。

使用:

List<Integer> ids =new ArrayList<>();
	ids.add(1);
	ids.add(3);

注意点:

传入类型为数组或者List时:collection属性值为array和list(或者collection)

传入的参数是多个的时,就需要把它们封装成一个Map了,当然单参数也可以封装成Map集合,这时候collection属性值就为Map的键。

传入参数POJO类型时:collection属性值就为该包装类需要进行遍历的数组或者集合的属性名。

7、<bind>元素

<bind>元素主要是针对数据进行拼接。

  • concat元素,只针对MySQL数据库有效,Oracle数据库就需要||进行拼接。
  • MyBatis的<bind>元素可以通过OGNL表达式来创建一个上下文变量。
<select id="findCustomerByIds" parameterType="list" resultType="com.lydms.po.Cusotmer">
  <!-- 也可以使用_parameter.getUsername()进行数据获取 -->
    <bind name="pattern_username" value="'%'+ username +'%'"/>
    select * from t_customer
    where
    username like #{pattern_username}
</select>
  • 使用<bind>元素定义一个名为name和pattern_username的变量,<bind>元素中value属性值就是拼接的查询字符串,其中_parameter.getUsername()(username)表示传递进来的参数。在sql中直接引用<bind>元素的name属性既可进行动态SQL引用。

使用:

Customer customer = new Customer();
customer.setUsername("j");//进行模糊查询使用

六、MyBatis的关联映射

1、关联关系简介

  • 一对一:在任意一方引入对方主键作为外键。
  • 一对多:在“多”的一方,添加“一”的一方的主键作为外键。
  • 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。

2、一对一关联

各元素含义:

<association>元素中,各元素含义:

  • column(数据表):指定数据库表中对应的字段。
  • property(实体类):指定映射到的实体类对象属性,与表字段一一对应。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子SQL语句,该属性用于关联映射汇总的嵌套查询。
  • fetchType:指定在关联查询时是否启用延迟加载。(fetchType有lazy和eager两个属性值。默认为lazy(默认关联映射延迟加载))
<result column="dealer_id" property="dealerId" jdbcType="VARCHAR"/>
    <!--方式一:嵌套查询-->
    <association property="card" column="card_id" javaType="com.lydms.po.Customer"
                 select="com.lydms.mapper.IdCardMapper"/>
    <!--方式二:嵌套结果-->
    <association property="card" javaType="com.lydms.po.Customer">
        <id column="card_id" property="id"/>
        <result column="code" property="code"/>
        <result column="dealer_id" property="dealerId" jdbcType="VARCHAR"/>
    </association>

重点:

MyBatis在映射文件中加载关联关系,主要通过两种方式:嵌套查询、嵌套结果。

嵌套查询:是通过执行另外一条SQL映射语句来返回预期的复杂类型。

嵌套结果:使用将查询结果进行嵌套映射来处理复杂查询结果集。

嵌套查询:

IdCardMapper.xml

<select id="findCodeById" parameterType="integer" resultType="IdCard">
	select * from tb_idcard where id=#{id}
</select>

PersonMapper.xml

<select id="findPersonById" parameterType="integer" resultMap="IdCardWithPersonResult">
	select * from tb_person where id =#{id}
</select>

<resultMap id="IdCardWithPersonResult" type="Person">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="sex" column="sex"/>
    <!--一对一:association使用select属性,引入另外一条SQL语句-->
    <association property="card" column="card_id" javaType="IdCard"
                 select="com.lydms.mapper.IdcardMapper.findCodeById"/>
</resultMap>
  • 嵌套查询的方法是先执行一个简单的SQL语句,然后在进行结果映射时,将关联对象在<association>元素中使用select属性执行另一条SQL语句(IdCardMapper.xml中的SQL)。
  • 嵌套查询会执行多条SQL,再去关联结果,这样会降低查询效率。
    在这里插入图片描述

嵌套结果:

    <select id="findPersonById" parameterType="integer" resultMap="IdCardWithPersonResult">
	  	select 
	  	  pl *, idcard.code 
	  	from 
	  	  tb_person p, tb_idcard idcard 
	  	where 
	  	  p.card_id = idcard.id 
	  	  and p.id =#{id}
	</select>

    <resultMap id="IdCardWithPersonResult" type="Person">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>

        <!--一对多:association 进行结果映射-->
        <association property="card" javaType="IdCard">
            <id property="id" column="id"/>
            <result property="code" column="code"/>
        </association>
    </resultMap>
  • 嵌套结果的方式只编写了一条复杂的多表关联的SQL语句,并且在<association>元素中继续使用相关子元素进行数据库表字段和实体类属性的一一映射。

3、一对多关联

<resultMap>元素中,包含了一个<collection>子元素,通过该元素处理一对多关系。

<resultMap id="f" type="integer">
    <!--方式一:嵌套查询-->
    <collection property="orderList" column="id" ofType="com.lydms.po.Orders"
                select="com.lydms.mapper.OrdersMapper.selectOrders"/>
  
    <!--方式二:嵌套结果-->
    <collection property="orderList" ofType="com.lydms.po.Orders">
        <id property="id" column="id"/>
        <result property="number" column="number"/>
    </collection>
</resultMap>

嵌套结果映射:

<select id="findUserWithOrders" parameterType="integer" resultMap="UserWithOrderResult">
	select 
	  u.*,o.id as orders_id, o.number 
	from 
	  tb_user u, tb_orders o 
	where 
	  u.id = o.user_id 
	  and u.id =#{id}
</select>
<resultMap id="UserWithOrderResult" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="address" column="address"/>
    <!--一对多关联映射:collection-->
    <!--ofType表示属性集合中元素的类型,List<Orders>属性即Orders类-->
    <collection property="ordersList" ofType="Orders">
        <id property="id" column="id"/>
        <result property="number" column="number"/>
    </collection>
</resultMap>

执行的是一对多关系映射,将另一个表的数据,映射到集合中。

4、多对多关联

在实际开发中,多对多的关联关系是非常常见的。

以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品属于多对多关联关系。在数据库中,多对多的关联关系使用一个中间表维护。中间表中的订单id作为外键参照订单表的id,商品表的id作为外键参照商品表的id。
在这里插入图片描述

嵌套查询:

实体类(Product.java)

import lombok.Data;
import java.util.List;

@Data
public class Product {
    //    商品id
    private Integer id;
    //    商品名称
    private String name;
    //    商品单价
    private Double price;
    //    与订单的关联属性
    private List<Orders> orders;
}

OrdersMapper.xml

<select id="findOrderswithProduct" parameterType="integer" resultMap="OrdersWithProductResult">
	select * from  tb_orders where id = #{id}
</select>
<!--多对多嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型-->
<resultMap id="OrdersWithProductResult" type="Orders">
    <id property="id" column="id"/>
    <result property="number" column="number"/>
    <collection property="productList" column="id" ofType="Product"
                select="com.lydms.mapper.ProductMapper.findProductById">
    </collection>
</resultMap>

ProductMapper.xml

<select id="findProductById" parameterType="integer" resultType="Product">
	select * from tb_product where id IN(
		select product_id from tb_ordersitem where orders_id = #{id}
	)
</select>

在这里插入图片描述

  • 嵌套查询方式执行了两条SQL语句,并查询出了订单及其关联的商品信息。

嵌套结果:

<!--多对多嵌套结果查询:查询某订单及其关联的商品详情-->
<select id="findOrderWithProduct" parameterType="integer" resultMap="OrdersWithProductResult">
	selet 0.*, p.id AS pid, p.name, p.prices
	FROM
		tb_orders p, tb_product p, tb_ordersitem oi
	WHERE
		oi.orders_id = o.id
	AND oi.product_id = p.id
	AND o.id = #{id}
</select>

<!--自定义手动映射类型-->
<resultMap id="OrdersWithProductResult" type="Orders">
	<id property="id" column="id"/>
	<result property="number" column="number"/>
	<!--多对多关联映射:collection-->
	<collection property="productList" ofType="Product">
		<id property="name" column="name"/>
		<result property="name" column="name"/>
		<result property="price" column="price"/>
	</collection>
</resultMap>
  • 只定义了一条查询SQL,通过该SQL即可查询出订单及其关联的商品信息。

七、MyBatis与Spring整合

  • 就是把数据库连接交给Spring管理。
  • 将dao实现交给Spring管理。
  • 包映射交给Spring管理
posted @ 2022-08-03 23:56  ah_lydms  阅读(33)  评论(0编辑  收藏  举报