SqlSession以及Spring与MyBatis整合准备所需要的JAR包要实现MyBatis与Spring的整合，很明显需要这两个框架的JAR包，但是只是使用这两个框架中所提供的JAR包是不够的，还需要配合其他包使用：Spring的JAR包MyBatis的JAR包Spring与MyBatis整合中间包mybatis-spring-x.x.x.jar数据库驱动JAR(以MYSQL为例)mysql-connector-java-x.x.x-bin.jar数据源所需的JAR(DBCP)commons-dbcp2-x.x.x.jar和commons-pool2-x.x.x.jar配置文件介绍关于resources文件夹我们在之前的项目中，几乎每次都会创建这个文件夹，然后Use as a source folder，那么这个文件夹到底是用来干啥的，凭什么使用它里面的文件是直接写文件名？实际上这个文件夹是专门存放你的应用所需资源的，如XML等配置文件。这个文件夹被标记为source folder后，在编译后，里面的文件会放到与编译好的文件相同目录里，所以你读取的直接使用文件名实际上是相对路径。pom.xml如果我们创建的是一个Maven项目，就可以在pom.xml里添加<properties></properties>标签来定义库版本，然后在下方的<dependencies></dependencies>中的<dependency></dependency>的<version></version>以${...}形式引用它。下面的XML文件省略了很多，仅为了演示。<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>4.12</junit.version> [...] </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> [...] </dependencies>db.propertiesdb.properties主要包含一些数据库连接信息，比如JDBC连接驱动、数据库账户名和密码等。我们在之前的项目中就已经使用过这个文件了，但我们一直没有详细介绍过其每条内容的含义（实际上语义很明显）jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_mybatis?useUnicode=true&characterEncoding=utf8 jdbc.username=db_mybatis jdbc.password=db_mybatis首先第一行jdbc.driver是指定数据库连接驱动。JDBC即Java数据库连接，它定义了一套关系型数据库的规则，没错实际上就是接口，然后实现并不是由Java官方进行的，而是交给各个数据库厂商来做，它们打包的库也就是JAR包，就被叫做数据库驱动，同时也意味着不同数据库就有不同的驱动实现。程序设计者无需去关心每个数据库驱动是怎么实现的，而是像你把手机充电器插到墙上的插座一样，把驱动按照接口给“插”进去。这就是面向接口编程的好处。你的程序只是一个调用者，数据库驱动就像一个代理人，你要先告诉这个代理人怎么操作，然后让这个代理人去真正操纵数据库（怎么操纵你是不关心的，只要满足你的操作需求即可）。你与这个代理人约定的通用交流方式就是刚才的接口，而代理人具体的操纵方式就是驱动的实现。我们所给的代码中很显然是MySQL的数据库驱动，后面的mysql.jdbc.Driver实际上是随你pom.xml添加的包一块引入的。第二行的是数据库连接URL，它实际上是数据库JDBC连接字符串的一部分。它的书写形式应该参考数据库驱动提供方的文档！jdbc说明这是一个JDBC的连接字符串mysql说明连接的数据库为MYSQLlocalhost所在部分，一般是数据库的网络地址或域名，localhost实际上是一个域名，它在你系统的HOST文件中被解析到了127.0.0.1（通常是这样，不绝对，你完全可以修改它），这个地址是一个回环地址，它实际上表示你机器本身。同理，我的MySQL如果在远程服务器192.168.10.2上面，这里就不再是localhost，而是192.168.10.2或者对应的域名。db_mybatis部分对应数据库名称，这个我们在之前创建过。useUnicode这里的值为true表示使用Unicode编码，它是国际标准字符集，将世界上每一种自然语言每个字符都定义为一个唯一编码，满足跨语言需要，尤其是包含中文的时候。characterEncoding部分是指定Unicode编码的存储方式。Unicode只是一个符号集，但并没有规定怎么存放。这里的存放方式为utf8，被定义为将码点编码为1到4个字节byte，具体取决于有效二进制位的数量。类似于useUnicode的参数还有很多，比如设置SSL等，这里不过多介绍，碰到了请参考文档或搜索引擎。jdbc.username和jdbc.password就很简单了，一般是具有权限的数据库用户的用户名和密码。分配一个或多个具有部分权限的用户是好习惯，而不是一直用root（它权限太大了，不安全）。app.xmlapp.xml为Spring配置文件，名称同样不固定，根据你自己来。对于内容的解释请看下方代码的注释： <?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="xxx.xxxx.xxx" /> <context:property-placeholder location="classpath:db.properties"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="transactionManager" class= "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="xx.xxx.xx.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> </beans>mybatis-config.xmlmybatis-config.xml为MyBatis配置文件，当然你可以不写它，将它的配置写在Spring配置文件中（写法不太一样），但不建议这么做，当文件内容特别多时不利于维护。<?xml version="1.0" encoding="UTF-8" ?> <configuration> <properties resource="db.properties" /> <settings> <setting name="lazyLoadingEnabled" value="false" /> <setting name="aggressiveLazyLoading" value="true" /> <setting name="mapUnderscoreToCamelCase" value="true" /> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <typeAliases> <package name="xx.xxx.xx.dao" /> </typeAliases> <mappers> <mapper resource="xx/xxx/xx/mapper/CustomerMapper.xml" /> </mappers> </configuration>log4j.properties日志的配置文件# Global logging configuration log4j.rootLogger=ERROR, stdout # MyBatis logging configuration... log4j.logger.top.cairbin.test5=DEBUG # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n进程、线程与CPU调度在开始Spring与MyBatis的整合之前，为了了解一些概念，我们来讲下进程与线程。什么是进程？进程是对程序过程的抽象。它是OS的动态执行单元，在传统OS上既是基本分配单元又是基本执行单元。举个不太严谨的例子，你在Windows下打开一个音乐播放器，随之你能在任务管理器中看到这一项，而这个正在运行的音乐播放器就是一个进程。线程实际上算是轻量级的进程，但二者还是不太相同，不过它们的相同点要多余不同点。它是看起来就像是对计算机创建进程的模拟，只不过这个模拟是在进程之中的。进程内存资源相对来讲较独立，而多个线程共享进程的内存资源。另外我们的CPU调度一般是抢占式的，我们在使用计算机的时候看起来没啥感觉，但实际上CPU一直在“抽疯”——在多个线程之间进行高速切换，这就带来一些问题。有些操作它不是原子的，即可分割为更小的操作，当一个活没有干完的时候，CPU便去干另一个活。同时，多个线程尝试对同一个变量进行修改的时候，顺序可能是乱的。这些都有可能导致线程不安全。什么是线程不安全？指多线程并发执行某个代码的时候，产生了逻辑上的错误，结果与预期值不同。当然，内存可变性以及Java编译器对指令优化也有可能导致这种情况。SqlSession对于MyBatis来讲实际上有三种SqlSession，我们来分别讲讲它们的区别和使用场景。首先看一张图片，描述再来描述它们的关系：DefaultSqlSessionDefaultSqlSession类是SqlSession接口的默认实现，它通常被使用于执行SQL操作数据库。//读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 构建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); try { MyDao myDao = sqlSession.selectOne("MyMapper.selectDao", 1); System.out.println(myDao); } finally { sqlSession.close(); }DefaultSqlSession是一个线程不安全的，也就是说它不能是单例。啥是单例？某些组件在整个程序运行时只要一个实例对象，多个实例化可能会报错。DefaultSqlSession即然不能是单例，那每次从工厂中获取一个不就行了，实际上这会带来额外开销和资源重用。另外，DefaultSqlSession还需要手动调用close()方法，这很容易忘记（虽然对于C++程序员是家常便饭），但是聪明的你肯定能用我们所学过的东西来解决这一问题吧——没错就是AOP。我们既想要单例，又要线程安全，还想要自动关闭怎么办？这就有了SqlSessionManager。SqlSessionManagerSqlSessionManager使用了JDK动态代理技术（我们之前讲过），动态生成代理对象sqlSessionProxy，并通过SqlSessionInterceptor来对DefaultSqlSession进行增强。虽然对于SqlSessionManager实际上还是创建非单例的DefaultSqlSession来执行方法，但SqlSessionManager可以是单例！那你可能会怼我，说多个DefaultSqlSession这不还是会造成额外开销和资源重用吗？SqlSessionManager还有另外一种形态，它会复用线程本地的DefaultSqlSession！线程不安全是由于多个线程之间共享DefaultSqlSession导致的，那我在同线程内“共享”（复用）我自己的DefaultSqlSession那不就解决线程安全问题了吗。这就大大提高了效率。治不了洋人还治不了你吗（雾）！SqlSessionTemplateSqlSessionTemplate是MyBatis与Spring整合时的线程安全SqlSession。SqlSessionTemplate实现线程安全的思路与SqlSessionManager相反，我既然自己管不了，我就让别人管——它交给SqlSessionUtils去获取SqlSession。但从本质上讲SqlSessionTemplate与SqlSessionManager还是一样的。SqlSessionUtils会先尝试从事务同步器中获取，获取不到再从工厂里要。而事务同步器本身就是一个线程本地变量管理器。所以SqlSessionTemplate与SqlSessionManager在实现线程安全这一点上殊途同归。但是二者在自动关闭，即自动执行close()方法的时候就有区别了。SqlSessionTemplate分两种情况：当获取的对象由事务同步管理器返回的时候，那关闭是交给Spring的。如果是从工厂里拿的，直接调用close()方法。Spring与MyBatis整合Spring与MyBatis整合方式分为两种：传统Dao方式Mapper接口方式传统Dao方式整合采用传统的Dao方式整合Spring和MyBatis框架，可采用mybatis-spring中所提供的SqlSessionTemplate类或者SqlSessionDaoSupport类来实现。由于这种方式在现在的开发中已经不常用了，所以这里仅做演示。@Repository public class CustomerDaoImpl extends SqlSessionDaoSupport implements ICustomerDao{ @Autowired public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){ super.sqlSessionFactory = sqlSessionFactory; } public void add(Customer customer){ [...] } }更细化的讲解可参考Spring | 整合MyBatis中SqlSessionTemplate和MapperScannerConfigurer类的使用Mapper接口方式传统Dao方式会产生大量重复代码，而且需要正确指定映射文件中的id。为了解决上述问题，我们采用Mapper的方式整合开发。基于MapperFactoryBean的整合MapperFactoryBean是MyBatis-Spring团队提供的一个用来根据Mapper接口生成Mapper对象的类，该类在配置文件中使用时可以配置一下参数mapperInterface用于指定接口SqlSessionFactory用于指定SqlSessionFactorySqlSessionTemplate用于指定SqlSessionTemplate，如果与SqlSessionFactory同时设定，则一般情况下只会启用SqlSessionTemplate。虽然使用Mapper接口编程的方式很简单，但是在具体使用的时候还是需要遵循一些规范：Mapper接口的名称和对应的XML映射文件名称必须一致XML映射文件中的namespace与Mapper接口的类路径相同Mapper接口方法名要和XML映射文件中定义的每个执行语句的id相同Mapper接口中的方法的输入参数类型和XML映射文件中的parameterType类型相同Mapper接口方法的输出类型要和XML映射文件的resultType类型相同基于MapperScannerConfigurer的方式使用上面的方式会使得配置文件臃肿，所以我们在做项目开发的时候一般是使用MapperScannerCongigurer的方式进行扫描。<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="top.cairbin.test6.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>我们新建一个项目top.cairbin.test6，至于哪些细节该注意你应当非常清楚了，这里就不多说了，如果不会请回去看之前的教程。对于配置文件我们也说的很清楚了，接下来直接给文件：pom.xml文件<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>top.cairbin</groupId> <artifactId>test6</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>test6</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <junit.version>4.12</junit.version> <spring.version>5.2.5.RELEASE</spring.version> <mybatis.version>3.5.4</mybatis.version> <mybatis.spring.version>2.0.4</mybatis.spring.version> <mysql.version>8.0.33</mysql.version> <commons-dbcp.version>2.7.0</commons-dbcp.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis.spring.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>${commons-dbcp.version}</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.4.0</version> </dependency> </dependencies> </project>db.properties文件jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_mybatis?useUnicode=true&characterEncoding=utf8 jdbc.username=db_mybatis jdbc.password=db_mybatisSpring配置文件app.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" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="top.cairbin.test6" /> <context:property-placeholder location="db.properties"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="transactionManager" class= "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="mybatis-config.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="top.cairbin.test6.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> </beans>mybatis-config.xml文件<?xml version="1.0" encoding="UTF-8" ?> <configuration> <properties resource="db.properties" /> <settings> <setting name="lazyLoadingEnabled" value="false" /> <setting name="aggressiveLazyLoading" value="true" /> <setting name="mapUnderscoreToCamelCase" value="true" /> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <typeAliases> <package name="top.cairbin.test6.dao" /> </typeAliases> <mappers> <mapper resource="top/cairbin/test6/mapper/CustomerMapper.xml" /> </mappers> </configuration>top.cairbin.test6.dao.Customer类package top.cairbin.test6.dao; public class Customer { private Integer id; // 主键id private String username; // 客户名称 private String jobs; // 职业 private String phone; // 电话 public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } public String getJobs() { return this.jobs; } public void setJobs(String jobs) { this.jobs = jobs; } public String getPhone() { return this.phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]"; } } top.cairbin.test6.mapper下的CustomerMapper.xml<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test6.mapper.CustomerMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test6.dao.Customer"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="jobs" jdbcType="VARCHAR" property="jobs" /> <result column="phone" jdbcType="VARCHAR" property="phone" /> </resultMap> <sql id="Base_Column_List"> id, username, jobs, phone </sql> select <include refid="Base_Column_List" /> from customer where id = #{id,jdbcType=INTEGER} </mapper>对应接口top.cairbin.test6.mapper.CustomerMapperpackage top.cairbin.test6.mapper; import top.cairbin.test6.dao.Customer; public interface CustomerMapper{ // 通过id查询客户 Customer selectByPrimaryKey(Integer id); }我们接下来实现Service层创建src/main/java下的包top.cairbin.test6.service然后在包中创建接口ICustomerServicepackage top.cairbin.test6.service; import top.cairbin.test6.dao.Customer; public interface ICustomerService { public Customer getCustomerByID(int id); }创建实现这个接口的类CustomerServiceImplpackage top.cairbin.test6.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import top.cairbin.test6.dao.Customer; import top.cairbin.test6.mapper.CustomerMapper; @Service public class CustomerServiceImpl implements ICustomerService{ @Autowired private CustomerMapper customerMapper; public Customer getCustomerByID(int id) { Customer customer = customerMapper.selectByPrimaryKey(id); return customer; } }在测试目录src/test/java下的top.cairbin.test6包创建测试类CustomerTestpackage top.cairbin.test6; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import top.cairbin.test6.dao.Customer; import top.cairbin.test6.service.ICustomerService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:app.xml" }) public class CustomerTest { @Autowired private ICustomerService customerService; @Test public void findTest() { Customer customer = customerService.getCustomerByID(1); System.out.println(customer); } }运行测试，得到结果
MyBatis关联映射为什么要关联映射实际开发中，对数据库操作常常会涉及多张表，所以在OOP中就涉及对象与对象的关联关系。针对多表操作，MyBatis提供关联映射。关联关系概述一对一：A类中定义B类的属性b，B类中定义A类属性a。一对多：一个A类类型对应多个B类。多对多：在A类中定义B的集合，B中定义A的集合。嵌套查询与嵌套结果嵌套查询是通过执行另外一条SQL映射语句来返回预期复杂类型：嵌套查询是在查询SQL嵌套一个子查询嵌套查询会执行多条SQL语句嵌套查询SQL语句编写相对简单嵌套结果是使用嵌套结果映射来处理的联合结果子集：嵌套结果是一个嵌套的多表查询SQL嵌套结果只会执行一条复杂的SQL语句嵌套结果SQL语句写起来相对麻烦对于嵌套查询有一个问题，那就是执行多条SQL语句导致性能开销很大。于是就有了MyBatis的延迟加载——fetchType。实践创建表我们还是用之前的账户连接数据库，终端命令可以如下mysql -u db_mybatis -p输入密码敲击回车，然后切换数据库到db_mybatisWindows用户可以用终端或者sqlyog执行下面语句如果你没有这个数据库请回到前面的章节创建它。USE db_mybatis;我们要使用一个新的customer表，所以把之前项目留下的删除掉DROP TABLE IF EXISTS customer;重新创建它CREATE TABLE customer( `id` int(32) NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL, `jobs` varchar(50) DEFAULT NULL, `phone` varchar(16) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;给这张表插入两条数据方便我们接下来写代码用INSERT INTO customer(`id`,`username`,`jobs`,`phone`) VALUES (1,'zhangsan','teacher','11111111111'),(2,'lisi','student','11111111112');创建一张idcard表，并设置主键（主码）为id如果之前存在请删除DROP TABLE IF EXISTS `idcard`;CREATE TABLE idcard( `id` int(11) NOT NULL AUTO_INCREMENT, `code` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;同样插入两条数据（身份证号是我瞎编的，为了防止巧合不一定符合真实格式，但长度与现实一样）INSERT INTO idcard(`id`,`code`) VALUES(1,'37010219800X051118'),(2,'370203199X09092222');创建persion表，并插入数据DROP TABLE IF EXISTS person; CREATE TABLE person( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `age` int(11) DEFAULT NULL, `card_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_person` (`card_id`), CONSTRAINT `FK_person` FOREIGN KEY (`card_id`) REFERENCES `idcard` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; INSERT INTO person(`id`,`name`,`age`,`card_id`) values (1,'张三',18,1),(2,'李四',18,2);创建product表并插入数据DROP TABLE IF EXISTS product; CREATE TABLE product( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) DEFAULT NULL, `price` double DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; INSERT INTO product(`id`,`name`,`price`) values (1,'笔记本电脑',8888.8),(2,'华为手机',6666.6);创建user表并插入数据DROP TABLE IF EXISTS user; CREATE TABLE user( `id` int(11) NOT NULL, `name` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO user(`id`,`name`,`password`) values (1,'zhangsan','654321'),(2,'lisi','123456');创建orders表DROP TABLE IF EXISTS orders; CREATE TABLE orders( `id` int(11) NOT NULL AUTO_INCREMENT, `number` varchar(32) DEFAULT NULL, `user_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_orders` (`user_id`), CONSTRAINT `FK_orders` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `orders`(`id`,`number`,`user_id`) VALUES (1,'201901',1);创建orderitem表，还是一样，如果之前存在先删除。这个表一定放后面，否则创建外键会报错。DROP TABLE IF EXISTS orderitem; CREATE TABLE orderitem( `id` int(11) NOT NULL AUTO_INCREMENT, `orders_id` int(11) DEFAULT NULL, `product_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_orderitem` (`orders_id`), KEY `FK_orderitem_product` (`product_id`), CONSTRAINT `FK_orderitem_product` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`), CONSTRAINT `FK_orderitem` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; INSERT INTO orderitem(`id`,`orders_id`,`product_id`) VALUES (1,1,1),(2,1,2);构建项目我们新创建一个Maven项目top.cairbin.test5pom.xml添加依赖包<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>top.cairbin</groupId> <artifactId>test5</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>test5</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> </project>创建与src平级的resources目录，并右键->Build Path->Use as Source Folder在里面添加一个数据库配置文件db.propertiesjdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_mybatis jdbc.username=db_mybatis jdbc.password=db_mybatis配置MyBatis，在同目录下创建mybatis-config.xml，这个文件会使用db.properties里面的配置，以${...}的方式调用，在<properties resource="db.properties" />里声明位置。<?xml version="1.0" encoding="UTF-8" ?> <configuration> <properties resource="db.properties" /> <settings> <setting name="lazyLoadingEnabled" value="false" /> <setting name="aggressiveLazyLoading" value="true" /> <setting name="mapUnderscoreToCamelCase" value="true" /> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <typeAliases> <package name="top.cairbin.test5" /> </typeAliases> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="top/cairbin/test5/mapper/CustomerMapper.xml" /> <mapper resource="top/cairbin/test5/mapper/UserMapper.xml" /> <mapper resource="top/cairbin/test5/mapper/IdCardMapper.xml" /> <mapper resource="top/cairbin/test5/mapper/OrdersMapper.xml" /> <mapper resource="top/cairbin/test5/mapper/PersonMapper.xml" /> <mapper resource="top/cairbin/test5/mapper/ProductMapper.xml" /> </mappers> </configuration>注意上方的<mapper>的目录，我们一会介绍。然后添加log4j的配置文件log4j.properties# Global logging configuration log4j.rootLogger=ERROR, stdout # MyBatis logging configuration... log4j.logger.top.cairbin.test5=DEBUG # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%nDao层我们先把对应的类创建了再说，到src/main/java下top.cairbin.test5创建一个子包，名称为top.cairbin.test5.dao在我们新创建的包下面编写若干个类首先是Person类，此时我们的IdCard还没创建，IDE提示错误很正常package top.cairbin.test5.dao; public class Person { private Integer id; private String name; private Integer age; private Integer cardId; private IdCard card; public void setId(Integer id) { this.id = id; } public Integer getId() { return this.id; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setAge(Integer age) { this.age = age; } public Integer getAge() { return this.age; } public Integer getCardId() { return cardId; } public void setCardId(Integer cardId) { this.cardId = cardId; } public void setCard(IdCard card) { this.card = card; } public IdCard getCard() { return this.card; } }接下来创建IdCardpackage top.cairbin.test5.dao; public class IdCard { private Integer id; private String code; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCode() { return code; } public void setCode(String code) { this.code = code == null ? null : code.trim(); } }然后是Customer类package top.cairbin.test5.dao; public class Customer { private Integer id; private String username; private String jobs; private String phone; 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 == null ? null : username.trim(); } public String getJobs() { return jobs; } public void setJobs(String jobs) { this.jobs = jobs == null ? null : jobs.trim(); } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone == null ? null : phone.trim(); } }接着是Orderspackage top.cairbin.test5.dao; package top.cairbin.test5.dao; import java.util.List; public class Orders { private Integer id; private String number; private Integer userId; private List<Product> productList; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number == null ? null : number.trim(); } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public void setProductList(List<Product> productList) { this.productList = productList; } public List<Product> getProductList(){ return this.productList; } }Product类package top.cairbin.test5.dao; public class Product { private Integer id; private String name; private Double price; 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 == null ? null : name.trim(); } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } }User类package top.cairbin.test5.dao; import java.util.List; public class User { private Integer id; private String name; private String password; private List<Orders> ordersList; 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 == null ? null : name.trim(); } public String getPassword() { return password; } public void setPassword(String password) { this.password = password == null ? null : password.trim(); } public void setOrdersList(List<Orders> ordersList) { this.ordersList = ordersList; } public List<Orders> getOrdersList(){ return this.ordersList; } }Util层这一层我们来放置一些工具类。创建包top.cairbin.test5.util然后在下面创建MyBatisHelper类package top.cairbin.test5.util; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class MyBatisHelper { private static SqlSessionFactory sqlSessionFactory = null; // 初始化SqlSessionFactory对象 static { try { // 使用MyBatis提供的Resources类加载MyBatis的配置文件 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); // 构建SqlSessionFactory工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { e.printStackTrace(); } } // 获取SqlSession对象的静态方法 public static SqlSession getSession() { return sqlSessionFactory.openSession(); } }Mapper层接下来我们再为top.cairbin.test5创建一个子包top.cairbin.test5.mapper，该层主要实现数据持久化。与上面不一样的是，我们这里定义的是接口首先是CustomerMapper接口package top.cairbin.test5.mapper; import top.cairbin.test5.dao.Customer; public interface CustomerMapper { int deleteByPrimaryKey(Integer id); int insert(Customer record); int insertSelective(Customer record); Customer selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Customer record); int updateByPrimaryKey(Customer record); }到这里你可能会问，谁去实现它们呢，还记得mybatis-config.xml里那些<mapper>吗，它们每一个都指定resource到Mapper层，也就是说我们要在这里写XML文件让MyBatis实现这些方法。第一个是CustomerMapper.xml，注意此处的namespace与<resultMap>的id，下文同理。<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test5.mapper.CustomerMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Customer"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="jobs" jdbcType="VARCHAR" property="jobs" /> <result column="phone" jdbcType="VARCHAR" property="phone" /> </resultMap> <sql id="Base_Column_List"> id, username, jobs, phone </sql> select <include refid="Base_Column_List" /> from customer where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from customer where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test5.dao.Customer"> insert into customer (id, username, jobs, phone) values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{jobs,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="top.cairbin.test5.dao.Customer"> insert into customer <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="username != null"> username, </if> <if test="jobs != null"> jobs, </if> <if test="phone != null"> phone, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="username != null"> #{username,jdbcType=VARCHAR}, </if> <if test="jobs != null"> #{jobs,jdbcType=VARCHAR}, </if> <if test="phone != null"> #{phone,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Customer"> update customer <set> <if test="username != null"> username = #{username,jdbcType=VARCHAR}, </if> <if test="jobs != null"> jobs = #{jobs,jdbcType=VARCHAR}, </if> <if test="phone != null"> phone = #{phone,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Customer"> update customer set username = #{username,jdbcType=VARCHAR}, jobs = #{jobs,jdbcType=VARCHAR}, phone = #{phone,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper>对了，请检查你XML中的方法的parameterType属性以及resultType对应类型的包是否正确！！！然后是IdCardMapper接口package top.cairbin.test5.mapper; import top.cairbin.test5.dao.IdCard; public interface IdCardMapper { int deleteByPrimaryKey(Integer id); int insert(IdCard record); int insertSelective(IdCard record); IdCard selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(IdCard record); int updateByPrimaryKey(IdCard record); }同样在IdCardMapper.xml里实现这些方法<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test5.mapper.IdCardMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test5.dao.IdCard"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="code" jdbcType="VARCHAR" property="code" /> </resultMap> <sql id="Base_Column_List"> id, code </sql> select <include refid="Base_Column_List" /> from idcard where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from idcard where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test5.dao.IdCard"> insert into idcard (id, code) values (#{id,jdbcType=INTEGER}, #{code,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="top.cairbin.test5.dao.IdCard"> insert into idcard <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="code != null"> code, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="code != null"> #{code,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.IdCard"> update idcard <set> <if test="code != null"> code = #{code,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.IdCard"> update idcard set code = #{code,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper>然后是ProductMapper接口以及ProductMapper.xmlpackage top.cairbin.test5.mapper; import top.cairbin.test5.dao.Product; public interface ProductMapper { int deleteByPrimaryKey(Integer id); int insert(Product record); int insertSelective(Product record); Product selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Product record); int updateByPrimaryKey(Product record); }<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test5.mapper.ProductMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Product"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="price" jdbcType="DOUBLE" property="price" /> </resultMap> <sql id="Base_Column_List"> id, name, price </sql> SELECT * from product where id IN( SELECT product_id FROM orderitem WHERE orders_id = #{id} ) select <include refid="Base_Column_List" /> from product where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from product where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test5.dao.Product"> insert into product (id, name, price ) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{price,jdbcType=DOUBLE} ) </insert> <insert id="insertSelective" parameterType="top.cairbin.test5.dao.Product"> insert into product <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="name != null"> name, </if> <if test="price != null"> price, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="price != null"> #{price,jdbcType=DOUBLE}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Product"> update product <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> <if test="price != null"> price = #{price,jdbcType=DOUBLE}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Product"> update product set name = #{name,jdbcType=VARCHAR}, price = #{price,jdbcType=DOUBLE} where id = #{id,jdbcType=INTEGER} </update> </mapper>对于剩下来的几个Dao层类对应的Mapper需要特别注意下，因为它们的类里都包含复合类型，这也是我们这一章节真正要学习的东西——关联映射。PersonMapper接口package top.cairbin.test5.mapper; import top.cairbin.test5.dao.Person; public interface PersonMapper { int deleteByPrimaryKey(Integer id); int insert(Person record); int insertSelective(Person record); Person selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Person record); int updateByPrimaryKey(Person record); }PersonMapper.xml文件，请重点关注findPersonById和findPersonById2这两个地方。<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test5.mapper.PersonMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Person"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="age" jdbcType="INTEGER" property="age" /> <result column="card_id" jdbcType="INTEGER" property="cardId" /> </resultMap> <sql id="Base_Column_List"> id, name, age, card_id </sql> SELECT * from person where id=#{id} <resultMap type="Person" id="IdCardWithPersonResult"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="age" column="age" /> <association property="card" column="card_id" javaType="top.cairbin.test5.dao.IdCard" select="top.cairbin.test5.mapper.IdCardMapper.selectByPrimaryKey" /> </resultMap> SELECT p.*,idcard.code from person p,idcard idcard where p.card_id=idcard.id and p.id= #{id} <resultMap type="Person" id="IdCardWithPersonResult2"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="age" column="age" /> <association property="card" javaType="IdCard"> <id property="id" column="id" /> <result property="code" column="code" /> </association> </resultMap> select <include refid="Base_Column_List" /> from person where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from person where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test5.dao.Person"> insert into person (id, name, age, card_id) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{cardId,jdbcType=INTEGER}) </insert> <insert id="insertSelective" parameterType="top.cairbin.test5.dao.Person"> insert into person <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="name != null"> name, </if> <if test="age != null"> age, </if> <if test="cardId != null"> card_id, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="age != null"> #{age,jdbcType=INTEGER}, </if> <if test="cardId != null"> #{cardId,jdbcType=INTEGER}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Person"> update person <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> <if test="age != null"> age = #{age,jdbcType=INTEGER}, </if> <if test="cardId != null"> card_id = #{cardId,jdbcType=INTEGER}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Person"> update person set name = #{name,jdbcType=VARCHAR}, age = #{age,jdbcType=INTEGER}, card_id = #{cardId,jdbcType=INTEGER} where id = #{id,jdbcType=INTEGER} </update> </mapper>对于上面这么长的文件，最重要的地方是这里 <association property="card" column="card_id" javaType="top.cairbin.test5.dao.IdCard" select="top.cairbin.test5.mapper.IdCardMapper.selectByPrimaryKey" />为了看到效果，我们在mybatis-config.xml里将延迟加载关掉。<settings> <setting name="lazyLoadingEnabled" value="false" /> <setting name="aggressiveLazyLoading" value="true"/> </settings>创建UserMapper接口和UserMapper.xmlpackage top.cairbin.test5.mapper; import top.cairbin.test5.dao.User; public interface UserMapper { int deleteByPrimaryKey(Integer id); int insert(User record); int insertSelective(User record); User selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); }<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test5.mapper.UserMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test5.dao.User"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="password" jdbcType="VARCHAR" property="password" /> <collection property="ordersList" column="id" ofType="Orders" select="top.cairbin.test5.mapper.OrdersMapper.findOrdersWithUser"> </collection> </resultMap> <sql id="Base_Column_List"> id, name, password </sql> select <include refid="Base_Column_List" /> from user where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from user where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test5.dao.User"> insert into user (id, name, password ) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR} ) </insert> <insert id="insertSelective" parameterType="top.cairbin.test5.dao.User"> insert into user <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="name != null"> name, </if> <if test="password != null"> password, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="password != null"> #{password,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.User"> update user <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> <if test="password != null"> password = #{password,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.User"> update user set name = #{name,jdbcType=VARCHAR}, password = #{password,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper>最后OrdersMapper和OrdersMapper.xmlpackage top.cairbin.test5.mapper; import top.cairbin.test5.dao.Orders; public interface OrdersMapper { int deleteByPrimaryKey(Integer id); int insert(Orders record); int insertSelective(Orders record); Orders selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Orders record); int updateByPrimaryKey(Orders record); }<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test5.mapper.OrdersMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test5.dao.Orders"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="number" jdbcType="VARCHAR" property="number" /> <result column="user_id" jdbcType="INTEGER" property="userId" /> </resultMap> <sql id="Base_Column_List"> id, number, user_id </sql> select * from orders WHERE user_id=#{id} select <include refid="Base_Column_List" /> from orders where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from orders where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test5.dao.Orders"> insert into orders (id, number, user_id ) values (#{id,jdbcType=INTEGER}, #{number,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER} ) </insert> <insert id="insertSelective" parameterType="top.cairbin.test5.dao.Orders"> insert into orders <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="number != null"> number, </if> <if test="userId != null"> user_id, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="number != null"> #{number,jdbcType=VARCHAR}, </if> <if test="userId != null"> #{userId,jdbcType=INTEGER}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test5.dao.Orders"> update orders <set> <if test="number != null"> number = #{number,jdbcType=VARCHAR}, </if> <if test="userId != null"> user_id = #{userId,jdbcType=INTEGER}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test5.dao.Orders"> update orders set number = #{number,jdbcType=VARCHAR}, user_id = #{userId,jdbcType=INTEGER} where id = #{id,jdbcType=INTEGER} </update> </mapper>我们编写测试类，在src/test/java下的top.cairbin.test5里PersonTest类来测试一对一的关系package top.cairbin.test5; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import top.cairbin.test5.dao.Person; import top.cairbin.test5.util.*; public class PersonTest { @Test public void findPersonByIdTest() { // 1、通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 2.使用MyBatis嵌套查询的方式查询id为1的人的信息 Person person = session.selectOne("top.cairbin.test5.mapper." + "PersonMapper.selectByPrimaryKey", 1); // 3、输出查询结果信息 System.out.println(person); // 4、关闭SqlSession session.close(); } //测试一对一（嵌套结果）： @Test public void findPersonByIdTest2() { // 1、通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 2.使用MyBatis嵌套查询的方式查询id为1的人的信息 Person person = session.selectOne("top.cairbin.test5.mapper." + "PersonMapper.findPersonById2", 1); // 3、输出查询结果信息 System.out.println(person); // 4、关闭SqlSession session.close(); } }点击运行，两个方法所用时间差距不小。我们再编写测试类UserTest来测试一对多package top.cairbin.test5; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import top.cairbin.test5.dao.User; import top.cairbin.test5.util.MyBatisHelper; public class UserTest { @Test public void findUserTest() { // 1、通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 2、查询id为1的用户信息，注意包名为mapper层的 User user = session.selectOne("top.cairbin.test5.mapper." + "UserMapper.selectByPrimaryKey", 1); // 3、输出查询结果信息 System.out.println(user); // 4、关闭SqlSession session.close(); } }最后编写OrdersTest来测试多对多package top.cairbin.test5; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import top.cairbin.test5.dao.Orders; import top.cairbin.test5.util.MyBatisHelper; public class OrdersTest { @Test public void findOrdersTest(){ // 1、通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 2、查询id为1的订单中的商品信息 Orders orders = session.selectOne("top.cairbin.test5.mapper." + "OrdersMapper.selectByPrimaryKey", 1); // 3、输出查询结果信息 System.out.println(orders); // 4、关闭SqlSession session.close(); } }总结在本章节，我们学习了MyBatis的关联映射。此外，你会发现我们写代码不再像以前一样只放在一个包里，而是创建好几个子包分层放置不同的代码，这有利于代码逻辑清晰并且方便维护，这种分层设计已经有一个正式项目的雏形了，当你写完这些东西的时候项目目录应该与我差不多。
动态SQL什么是动态SQL动态SQL是MyBatis强大特性之一，MyBatis3采用了功能强大的基于OGNL的表达式来完成SQL。动态SQL主要元素常用的动态SQL主要元素如下：如果单独对以上元素解释理解起来还是比较费力的，接下来还是以代码的形式展现。实践基础部分我们还是用之前的项目top.cairbin.test4，我们把src/main/java目录下的top.cairbin.test4包中的CustomerMapper.xml文件里的<mapper namespace="top.cairbin.test4.CustomerMapper"></mapper>中间的内容全部删除，最终效果看上去应该与下面一致：<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test4.CustomerMapper"> </mapper>然后把src/test/java目录下的top.cairbin.test4中的测试类MyBatisTest里面的测试方法也删除掉package top.cairbin.test4; 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 top.cairbin.test4.*; public class MyBatisTest { }我们先来编写表与对象的映射关系，在CustomerMapper.xml的<mapper>元素之间填写如下内容（注意包名与你的一致）： <resultMap id="BaseResultMap" type="top.cairbin.test4.Customer"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="jobs" jdbcType="VARCHAR" property="jobs" /> <result column="phone" jdbcType="VARCHAR" property="phone" /> </resultMap> <sql id="Base_Column_List"> id, username, jobs, phone </sql>看起来效果应该跟下面一样，不过在后文中这个文件会越写越长，由于篇幅有限，我们每次仅给出增添部分的代码，而不是每次修改都粘贴全部配置到这里来<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test4.CustomerMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test4.Customer"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="jobs" jdbcType="VARCHAR" property="jobs" /> <result column="phone" jdbcType="VARCHAR" property="phone" /> </resultMap> <sql id="Base_Column_List"> id, username, jobs, phone </sql> </mapper>为了方便操作SqlSession，我们在src/main/java的top.cairbin.test4包下写一个工具类，名称为MyBatisHelperpackage top.cairbin.test4; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class MyBatisHelper { private static SqlSessionFactory sqlSessionFactory = null; // 初始化SqlSessionFactory对象 static { try { // 使用MyBatis提供的Resources类加载MyBatis的配置文件 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); // 构建SqlSessionFactory工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { e.printStackTrace(); } } // 获取SqlSession对象的静态方法 public static SqlSession getSession() { return sqlSessionFactory.openSession(); } }你可能对static{...}这段代码有所疑惑，但是你在学习Java语言的时候应该涉及过才对，这也算一种技巧吧，即然碰到了我们就讲一下。还记不记得static关键字的特性？被它修饰的代码不依赖于任何实例化的对象，它们的内存是固定且唯一的，并且不会被Java的垃圾回收机制回收，直至程序结束。也就是说当它们的内存一旦被确认下来，在程序需运行的过程中，所指向的内存位置都是同一地址。当你定义一个类MyClass，类内有个public静态成员staticTest，你并不用(new MyClass()).staticTest，而是直接以MyClass.staticTest的方式去调用。因为对于每一个MyClass它所指向的staticTest都是同一个。那么跟我们要说的static修饰的代码块有什么关系呢？想一想你每次调用方法，都要连接一次数据库，操作完后被Java的回收机制释放掉，当调用次数过多的时候对于连接方和被连接方开销都是巨大的。那么这时候你就会想，能不能让这个过程只执行一次而且不会被Java的回收机制释放呢？这时候就可以利用我们的静态代码块了。无论你new MyBatisHelper()多少次，它只会加载一次且在整个程序运行时有效！编写方法回到正题，我们来编写第一个方法，插入Customer我们在CustomerMapper.xml中<mapper></mapper>标签中间，在原来的基础上追加以下内容（这句话描述的很准确了）<insert id="insertSelective" parameterType="top.cairbin.test4.Customer"> insert into customer <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="username != null"> username, </if> <if test="jobs != null"> jobs, </if> <if test="phone != null"> phone, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="username != null"> #{username,jdbcType=VARCHAR}, </if> <if test="jobs != null"> #{jobs,jdbcType=VARCHAR}, </if> <if test="phone != null"> #{phone,jdbcType=VARCHAR}, </if> </trim> </insert>然后在MyBatisTest测试类里添加测试方法（这里就用到了MyBatisHelper）：@Test public void insertCustomerTest() { SqlSession session = MyBatisHelper.getSession(); Customer cc = new Customer(); cc.setUsername("cairbin"); cc.setJobs("student"); cc.setPhone("888888"); int insert = session.insert("top.cairbin.test4.CustomerMapper.insertSelective", cc); session.commit(); System.out.println(insert + "," + cc); session.close(); }我们运行一下，看看结果看样子是成功了。来看看数据库里，操作还是那样，我这里仅给个结果，你可以跳过这一步，因为我们接下来要编写查询方法。继续编写CustomerMapper.xml文件，我们这里用到了 标签，并且同样用<if>来对数据进行判断。这里应当提一句，看一下 的resultMap属性，是不是正好是<resultMap>标签的id，对应的Java类也就是Customer，同样对应数据库的表customer。 select * from customer where 1=1 <if test="username !=null and username !=''"> and username like concat('%',#{username}, '%') </if> <if test="jobs !=null and jobs !=''"> and jobs= #{jobs} </if> 继续编写测试方法，结合XML，我们可知这个是测试根据username和jobs来查询数据的。@Test public void findCustomerByNameAndJobsTest() { // 通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 创建Customer对象，封装需要组合查询的条件 Customer customer = new Customer(); customer.setUsername("cairbin"); customer.setJobs("student"); // 执行SqlSession的查询方法，返回结果集 List<Customer> customers = session .selectList("top.cairbin.test4" + ".CustomerMapper.findCustomerByNameAndJobs", customer); // 输出查询结果信息 for (Customer customer2 : customers) { // 打印输出结果 System.out.println(customer2); } // 关闭SqlSession session.close(); }大致原理已经知道了，剩下的我们就不做演示了，自己尝试去写一写吧。这里给出完全的CustomerMapper.xml和MyBatis.javaCustomerMapper.xml<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test4.CustomerMapper"> <resultMap id="BaseResultMap" type="top.cairbin.test4.Customer"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="jobs" jdbcType="VARCHAR" property="jobs" /> <result column="phone" jdbcType="VARCHAR" property="phone" /> </resultMap> <sql id="Base_Column_List"> id, username, jobs, phone </sql> select <include refid="Base_Column_List" /> from customer where id = #{id,jdbcType=INTEGER} <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from customer where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="top.cairbin.test4.Customer" useGeneratedKeys="true" keyProperty="id"> insert into customer (id, username, jobs, phone) values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{jobs,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="top.cairbin.test4.Customer"> insert into customer <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="username != null"> username, </if> <if test="jobs != null"> jobs, </if> <if test="phone != null"> phone, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="username != null"> #{username,jdbcType=VARCHAR}, </if> <if test="jobs != null"> #{jobs,jdbcType=VARCHAR}, </if> <if test="phone != null"> #{phone,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="top.cairbin.test4.Customer"> update customer <set> <if test="username != null"> username = #{username,jdbcType=VARCHAR}, </if> <if test="jobs != null"> jobs = #{jobs,jdbcType=VARCHAR}, </if> <if test="phone != null"> phone = #{phone,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="top.cairbin.test4.Customer"> update customer set username = #{username,jdbcType=VARCHAR}, jobs = #{jobs,jdbcType=VARCHAR}, phone = #{phone,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> select * from customer where 1=1 select * from customer where 1=1 <if test="username !=null and username !=''"> and username like concat('%',#{username}, '%') </if> <if test="jobs !=null and jobs !=''"> and jobs= #{jobs} </if> select * from 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 * from customer where id in <foreach item="id" index="index" collection="list" open="(" separator="," close=")"> #{id} </foreach> <bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'" /> select * from customer where username like #{pattern_username} </mapper>MyBatisTest.javapackage top.cairbin.test4; import java.io.InputStream; import java.util.ArrayList; 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 top.cairbin.test4.*; public class MyBatisTest { @Test public void insertCustomerTest() { SqlSession session = MyBatisHelper.getSession(); Customer cc = new Customer(); cc.setUsername("cairbin"); cc.setJobs("student"); cc.setPhone("888888"); int insert = session.insert("top.cairbin.test4.CustomerMapper.insertSelective", cc); session.commit(); System.out.println(insert + "," + cc); session.close(); } @Test public void findCustomerByNameAndJobsTest() { // 通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 创建Customer对象，封装需要组合查询的条件 Customer customer = new Customer(); customer.setUsername("cairbin"); customer.setJobs("student"); // 执行SqlSession的查询方法，返回结果集 List<Customer> customers = session .selectList("top.cairbin.test4" + ".CustomerMapper.findCustomerByNameAndJobs", customer); // 输出查询结果信息 for (Customer customer2 : customers) { // 打印输出结果 System.out.println(customer2); } // 关闭SqlSession session.close(); } /** * 根据客户姓名或职业查询客户信息列表 */ @Test public void findCustomerByNameOrJobsTest() { // 通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 创建Customer对象，封装需要组合查询的条件 Customer customer = new Customer(); customer.setUsername("cairbin"); // customer.setJobs("teacher"); // 执行SqlSession的查询方法，返回结果集 List<Customer> customers = session.selectList("top.cairbin.test4" + ".CustomerMapper.findCustomerByNameOrJobs", customer); // 输出查询结果信息 for (Customer customer2 : customers) { // 打印输出结果 System.out.println(customer2); } // 关闭SqlSession session.close(); } /** * 更新客户 */ @Test public void updateCustomerTest() { // 获取SqlSession SqlSession sqlSession = MyBatisHelper.getSession(); // 创建Customer对象，并向对象中添加数据 Customer customer = new Customer(); customer.setId(1); customer.setPhone("13311111234"); // 执行SqlSession的更新方法，返回的是SQL语句影响的行数 int rows = sqlSession.update("top.cairbin.test4" + ".CustomerMapper.updateByPrimaryKeySelective", customer); // 通过返回结果判断更新操作是否执行成功 if (rows > 0) { System.out.println("您成功修改了" + rows + "条数据！"); } else { System.out.println("执行修改操作失败！！！"); } // 提交事务 sqlSession.commit(); // 关闭SqlSession sqlSession.close(); } /** * 根据客户编号批量查询客户信息 */ @Test public void findCustomerByIdsTest() { // 获取SqlSession SqlSession session = MyBatisHelper.getSession(); // 创建List集合，封装查询id List<Integer> ids = new ArrayList<Integer>(); ids.add(1); ids.add(2); // 执行SqlSession的查询方法，返回结果集 List<Customer> customers = session.selectList("top.cairbin.test4" + ".CustomerMapper.findCustomerByIds", ids); // 输出查询结果信息 for (Customer customer : customers) { // 打印输出结果 System.out.println(customer); } // 关闭SqlSession session.close(); } /** * bind元素的使用：根据客户名模糊查询客户信息 */ @Test public void findCustomerByNameTest() { // 通过工具类生成SqlSession对象 SqlSession session = MyBatisHelper.getSession(); // 创建Customer对象，封装查询的条件 Customer customer = new Customer(); customer.setUsername("cair"); // 执行sqlSession的查询方法，返回结果集 List<Customer> customers = session.selectList("top.cairbin.test4" + ".CustomerMapper.findCustomerByName", customer); // 输出查询结果信息 for (Customer customer2 : customers) { // 打印输出结果 System.out.println(customer2); } // 关闭SqlSession session.close(); } }
了解MyBatis什么是MyBatisMyBatis 是一款优秀的持久层框架，它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO（Plain Old Java Objects，普通老式 Java 对象）为数据库中的记录。MyBatis中文文档 可参考 https://mybatis.net.cn/。MyBatis是不是ORM？什么是ORM？即Object-Relationl Mapping，它的作用是在关系型数据库和对象之间作一个映射，这样，我们在具体的操作数据库的时候，就不需要再去写复杂的SQL语句。JPA是orm框架标准，MyBatis没有实现JPA，和orm框架的设计思路完全不一样。MyBatis是拥抱sql，而orm则更靠近面向对象。Mybatis是sql mapping框架而不是orm框架，当然orm和Mybatis都是持久层框架。MyBatis工作原理使用MyBatis废话不多说，我们直接上手用下。创建数据库与表我们来创建一个名称为db_mybatis的数据库还是老样子，我这里以终端为例，你可以用sqlyog等工具。mysql -uroot有密码的话mysql -u root -p执行SQL语句创建数据库CREATE DATABASE db_mybatis;创建管理该数据库的用户db_mybatis，密码为db_mybatis，访问主机为本机可访问localhost，如果远程访问请改为%。CREATE USER 'db_mybatis'@'localhost' IDENTIFIED BY 'db_mybatis';给它管理db_mybatis的所有权限GRANT ALL ON db_mybatis.* TO 'db_mybatis'@'localhost';刷新权限flush privileges;如果你用的MySQL8.0及以上版本，客户端版本较老，由于加密算法问题需要追加以下语句：ALERT USER 'db_mybatis'@'localhost' IDENTIFIED WITH mysql_native_password by 'db_mybatis';退出MySQL控制台，返回PC终端exit;我们用刚创建的用户连接下mysqlmysql -u db_mybatis -p然后出现提示，输入密码db_mybatis敲击回车，如果密码跟我不一样，这里与你上面的设置一致。切换数据库到db_mybatisUSE db_mybatis;如果表名称已经存在，会影响我们创建，如果你之前不太重要的话就直接删了，如果想要保留那么新建的表就要修改名字DROP TABLE IF EXISTS `customer`;我们创建一张表，名称为customer，主键为id如果用标准的SQL语句来写的话，我这里版本为MySQL5.7，可能会报错如果报错，就替换成以下语句CREATE TABLE customer ( id int(32) AUTO_INCREMENT, username varchar(50) DEFAULT NULL, jobs varchar(50) DEFAULT NULL, phone varchar(16) DEFAULT NULL, PRIMARY KEY(`id`) );我们插入条数据（这里的电话号码是我瞎编的）INSERT INTO customer(id,username,jobs,phone) VALUES(1,'joy','doctor','15544433322');通过SELECT语句查看下，看来是成功的SELECT * FROM customer;创建项目我们创建一个Maven项目，不会的请回Spring初步部分看看。项目名称为top.cairbin.test4，然后修改pom.xml添加依赖项<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>top.cairbin</groupId> <artifactId>test4</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>test4</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.4.0</version> </dependency> </dependencies> </project>我们创建一个resources文件夹，并use as source folder在里面创建一个log4j.properties，可以让我们在控制台看到SQL语句# Global logging configuration log4j.rootLogger=ERROR, stdout # MyBatis logging configuration... log4j.logger.top.cairbin.test4=DEBUG # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n在src/main/java下的top.cairbin.test4包下创建一个持久化类Customerpackage top.cairbin.test4; public class Customer { private Integer id; // 主键id private String username; // 客户名称 private String jobs; // 职业 private String phone; // 电话 public void setId(Integer id) { this.id = id; } public Integer getId() { return this.id; } public void setUsername(String username) { this.username = username; } public String getUsername() { return username; } public void setJobs(String jobs) { this.jobs = jobs; } public String getJobs() { return this.jobs; } public void setPhone(String phone) { this.phone = phone; } public String getPhone() { return this.phone; } //为了方便测试时输出结果，建议生成toString()方法。 @Override public String toString() { return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]"; } }在top.cairbin.test4下创建一个映射文件CustomerMapper.xml，注意<mapper>标签的namespace属性。<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test4.CustomerMapper"> select * from customer where id = #{id} </mapper>在resources文件夹里新建一个mybatis核心配置文件mybatis-config.xml<?xml version="1.0" encoding="UTF-8" ?> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true" /> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis" /> <property name="username" value="db_mybatis" /> <property name="password" value="db_mybatis" /> </dataSource> </environment> </environments> <mappers> <mapper resource="top/cairbin/test4/CustomerMapper.xml" /> </mappers> </configuration>注意这里的位置<mappers> <mapper resource="top/cairbin/test4/CustomerMapper.xml" /> </mappers>我们在src/test/java目录下创建测试类MyBatisTestpackage top.cairbin.test4; 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 top.cairbin.test4.*; public class MyBatisTest { /** * 根据客户编号查询客户信息 */ @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执行映射文件中定义的SQL，并返回映射结果 Customer customer = sqlSession.selectOne("top.cairbin.test4" + ".CustomerMapper. findCustomerById", 1); // 打印输出结果，Customer类中记得生成toString()方法。 System.out.println(customer.toString()); // 5、关闭SqlSession sqlSession.close(); } }注意这条语句Customer customer = sqlSession.selectOne("top.cairbin.test4" + ".CustomerMapper. findCustomerById", 1);我们正是在调用刚才在CustomerMapper.xml里编写的方法，对应 标签的id属性。点击运行看到输出结果在CustomerMapper.xml映射文件中添加模糊查询,添加,更新和删除的配置,在测试类中添加对应的测试方法,然后进行测试。注意每一个方法的parameterType和resultType属性对应的包。<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="top.cairbin.test4.CustomerMapper"> select * from customer where id = #{id} select * from customer where username like concat('%',#{value},'%') <insert id="addCustomer" parameterType="top.cairbin.test4.Customer"> insert into customer(username,jobs,phone) values(#{username},#{jobs},#{phone}) </insert> <update id="updateCustomer" parameterType="top.cairbin.test4.Customer"> update customer set username=#{username},jobs=#{jobs},phone=#{phone} where id=#{id} </update> <delete id="deleteCustomer" parameterType="Integer"> delete from customer where id=#{id} </delete> </mapper>同样的测试类修改为（请注意代码中的包名）package top.cairbin.test4; 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 top.cairbin.test4.*; public class MyBatisTest { /** * 根据客户编号查询客户信息 */ @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执行映射文件中定义的SQL，并返回映射结果 Customer customer = sqlSession.selectOne("top.cairbin.test4" + ".CustomerMapper. findCustomerById", 1); // 打印输出结果，Customer类中记得生成toString()方法。 System.out.println(customer.toString()); // 5、关闭SqlSession 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("top.cairbin.test4" + ".CustomerMapper.findCustomerByName", "j"); for (Customer customer : customers) { //打印输出结果集 System.out.println(customer); } // 5、关闭SqlSession 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("top.cairbin.test4" + ".CustomerMapper.addCustomer", customer); // 4.3通过返回结果判断插入操作是否执行成功 if(rows > 0){ System.out.println("您成功插入了"+rows+"条数据！"); }else{ System.out.println("执行插入操作失败！！！"); } // 4.4提交事务 sqlSession.commit(); // 5、关闭SqlSession 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(); // 4、SqlSession执行更新操作 // 4.1创建Customer对象，对对象中的数据进行模拟更新 Customer customer = new Customer(); customer.setId(2); //注意该id必须是你数据库中已有的id. customer.setUsername("rose"); customer.setJobs("programmer"); customer.setPhone("13311111111"); // 4.2执行SqlSession的更新方法，返回的是SQL语句影响的行数 int rows = sqlSession.update("top.cairbin.test4" + ".CustomerMapper.updateCustomer", customer); // 4.3通过返回结果判断更新操作是否执行成功 if(rows > 0){ System.out.println("您成功修改了"+rows+"条数据！"); }else{ System.out.println("执行修改操作失败！！！"); } // 4.4提交事务 sqlSession.commit(); // 5、关闭SqlSession sqlSession.close(); } /** * 删除客户 */ @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("top.cairbin.test4" + ".CustomerMapper.deleteCustomer", 2); //注意该id必须是你数据库中已有的id. // 4.2通过返回结果判断删除操作是否执行成功 if(rows > 0){ System.out.println("您成功删除了"+rows+"条数据！"); }else{ System.out.println("执行删除操作失败！！！"); } // 4.3提交事务 sqlSession.commit(); // 5、关闭SqlSession sqlSession.close(); } }运行，然后测试即可。