一、 准备工作
1. maven配置
在pom.xml
配置静态资源过滤,使mapper.xml文件不必受maven的约束
<build>
<resources><!--配置静态资源过滤-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin><!--跳过单元测试 -->
<version>2.12.4</version>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<!-- 这个建议也配上,第一条不配后期xml的中文注释可能会使程序报错,下面两条是jdk -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
pom坐标
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--8.0驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- ognl -->
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.2.14</version>
</dependency>
<!-- commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.0</version>
</dependency>
很奇怪第一次控制台已经成功打印了结果,同一个方法第二次运行无法编译,pom.xml中配置下面这个就好了
<build>
<plugins>
<plugin>
<version>2.12.4</version>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip><!--跳过单元测试 -->
</configuration>
</plugin>
</plugins>
</build>
2. 配置文件模板
mybatis所需配置文件模板
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="命名空间">
</mapper>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<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/mybatis01"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--<mapper resource="com/ligong/dao/UserMapper.xml"/>-->
<!--<package name="com.ligong.mapper" />-->
</mappers>
</configuration>
log4j.properties
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\\MyCode\\Logs\\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
db.properties
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/数据库?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
jdbc.username=root
jdbc.password=root
jdbc.initialSize=5
jdbc.maxActive=10
jdbc.maxWait=3000
二、第一天
mybatis框架也被称为ORM(对象关系映射)框架。ORM就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中
mybatis与hibernate的区别 :
Hibernate
是一个全表映射的框架。
通常开发者只需定义好持久化对象到数据库表的映射关系,就可以通过Hibernate提供的方法完成持久层操作。
开发者并不需要熟练的掌握SQL语句的编写,Hibernate会根据制定的存储逻辑,自动的生成对应的SQL,并调用JDBC接口来执行,所以其开发效率会高于MyBatis。
Hibernate也存在一些缺点,例如它在多表关联时,对SQL查询的支持较差;更新数据时,需要发送所有字段;不支持存储过程;不能通过优化SQL来优化性能等。
MyBatis
是一个半自动映射的框架。
“半自动”是相对于Hibernate全表映射而言的,MyBatis需要手动匹配提供POJO、SQL和映射关系,而Hibernate只需提供POJO和映射关系即可。
与Hibernate相比,虽然使用MyBatis手动编写SQL要比使用Hibernate的工作量大,但MyBatis可以配置动态SQL并优化SQL,可以通过配置决定SQL的映射规则,它还支持存储过程等。对于一些复杂的和需要优化性能的项目来说,显然使用MyBatis更加合适
1.crud入门程序
在创建pojo类时,需要注意类的属性尽量和数据库中对应的字段保持一致
注意要在核心配置文件的标签中指定mapper的路径,他的执行流程大约就是,首选你运行测试类代码,然后读取mybatis配置文件,找到注册在配置文件的mapper的位置,匹配命名空间和sql语句的id,传入参数,获取结果映射到你的实体类中
对象关系映射:将数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就可以实现操作数据库表
入门程序代码
// MyTest.java
@Test
public void findAllUser() throws IOException {
InputStream is = null;
SqlSessionFactory sqlSessionFactory = null;
SqlSession sqlSession = null;
try {
is = Resources.getResourceAsStream("mybaits-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
sqlSession = sqlSessionFactory.openSession();
//通过命名空间的方式获取目标对象
//List<User> users = sqlSession.selectList("com.ligong.dao.UserMapper.findAllUser");
//通过对应接口的class文件获得目标对象,需遵守Mapper代理开发的规范
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findAllUser();
for (User user : users) {
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
is.close();
}
}
//dao.UserMapper.java
public interface UserMapper {
// 基于注解的开发,不需要写映射文件,需要在mybatis-config.xml中注册UserMapper接口的位置
//@Select("select * from user")
List<User> findAllUser();
}
//dao.UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ligong.dao.UserMapper">
<select id="findAllUser" resultType="com.ligong.domain.User">
select * from user
</select>
</mapper>
//domain.User.java
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public Date getBirthday() {return birthday;}
public void setBirthday(Date birthday) {this.birthday = birthday;}
public String getSex() {return sex;}
public void setSex(String sex) {this.sex = sex;}
public String getAddress() {return address;}
public void setAddress(String address) {this.address = address;}
}
//log4j.properties
# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
# 在此处添加你要跟踪的包路径
log4j.logger.com.ligong=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
//mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis01?serverTimezone=UTC&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/ligong/dao/UserMapper.xml"/>
<!--所有使用mybatis注解开发的接口都需要在这里注册-->
<!--<mapper class="com.ligong.dao.UserMapper"/>-->
</mappers>
</configuration>
Mapper代理开发的规范
- Mapper接口的名称和对应的Mapper.xml映射文件的名称必须一致
- Mapper.xml文件的namespace与Mapper接口的类路径相同(即接口文件和映射文件需要放在同一个包下)
- Mapper接口中的方法名与Mapper.xml中定义的每个执行语句的id相同
- Mapper接口中方法的输入参数类型和Mapper.xml中定义的每个sql的parameterType类型相同
- Mapper接口方法的输出参数类型和要Mapper.xml中定义的每个sql的resultType的类型相同
SqlSession对象提供了crud以及提交事务的各种方法,根据不同的需要使用不同的方法
增删改对应的insert(),delete(),update()方法
SqlSession是Mybatis框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作。底层封装了jdbc的连接
每一个线程都应该有一个自己的SqlSession实例,而且该实例是不能被共享的.同时,SqlSession实例也是线程不安全的,因此其使用单位最好在一次请求或一个方法中,绝不能将其放在一个类的静态字段、实例字段或任何类型的管理范围中使用。
2.MyBatis的核心配置文件
在MyBatis框架的核心配置文件中,元素是配置文件的根元素,其他元素都要在元素内配置。而且必须按照由上到下的顺序进行编写 ,mybatis按照下面的顺序进行解析
<configuration>
<properties resource="配置文件"/>
<!--settings元素主要用于改变MyBatis运行时的行为,例如开启二级缓存、开启延迟加载等-->
<settings></settings>
<typeAliases></typeAliases><!--配置别名-->
<typeHanglers></typeHanglers><!--在java类型与jdbc类型进行转换的,了解即可-->
<objectFactory></objectFactory><!--用来实例化目标类,使用默认的即可-->
<plugins></plugins><!--配置用户所开发的插件-->
<environments>
<environments>
<transactionManager></transactionManager><!--配置事务管理-->
<dataSource></dataSource><!--配置连接池-->
</environments>
</environments>
<databaseIdProvider></databaseIdProvider>
<mappers></mappers><!--配置mapper文件位置-->
</configuration>
什么是缓存:存在于内存中的临时数据。
为什么使用缓存:减少与数据库的交互次数,提高执行效率。
一级缓存 : 指的是Mybatis中的SqlSession对象的缓存,当我们执行查询之后,查询的结果会同事存入到SqlSession为我们提供一块区域中,该区域是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话就直接拿来用,当SqSession对象消失后,mybatis的一级缓存也就消失了
一级缓存默认开启,缓存在Sqlsession里,sqlSession.clearCache()此方法可以清空缓存,同时,当调用SqlSession的增删改,commit(),close()方法就会清空一级缓存
二级缓存 : 即SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存
二级缓存需要手动开启,缓存在SqlSessionFactory里,二级缓存查询的对象需要实现序列化接口(Serializable),并且需要在映射文件中使用开启缓存功能
<settings>
<setting name="cacheEnabled" value="true"/><!--手动开启二级缓存,默认为false-->
</settings>
typeAliases元素用于为配置文件中的Java类型(全限定类名)设置一个简短的名字,即配置别名.别名的设置与XML配置相关,其使用的意义在于减少全限定类名的冗余,别名不区分大小写.
<typeAliases>
<typeAlias alias="user" type="全限定类名" />
</typeAliases>
<!--如果POJO类过多时,可以通过自动扫描包的形式自定义别名,具体如下-->
<!--每个全限定类名的别名对应其类型-->
<typeAliases>
<package name="包名" />
</typeAliases>
plugins元素的作用给就是配置用户所开发的插件,Mybatis允许在一映射语句执行过程中的某一点进行拦截调用,这种拦截调用是通过插件来实现的.
如果用户向要进行插件开发,必须要了解内部运行原理,因为在视图修改或重写已有方法的行为时,很可能会破坏MyBatis原有的核心模块.
mappers元素用于指定MyBatis映射文件的位置,一般使用几种方法引入映射文件
<!--使用类路径引入-->
<mappers>
<mapper resource="com/ligong/mapper/UserMapper.xml" />
</mappers>
<!--使用接口类引入-->
<mappers>
<mapper class="com.ligong.mapper.UserMapper" />
</mappers>
<!--使用包名引入-->
<mappers>
<package name="com.ligong.mapper" />
</mappers>
3.Mapper映射文件
在mapper映射文件中主要有以下几种元素
<select> <insert> <update> <delete> <sql> <cache> <cache-ref> <resultMap>
<sql> 用来定义一部分SQL,然后可被其他语句引用此SQL
<cache> 给定命名空间的缓存配置
<cache-ref> 其他命名空间缓存配置的引用
<resultMap> 用来描述如何从数据库结果集中来加载对象
insert元素的属性,以下三个属性也仅对insert
和update
有效,
属性 | 说明 |
---|---|
keyProperty | 此属性的作用时将插入或更新操作时的返回值赋值给PO类的某个属性名,通常会 设置为主键对应的属性名.如果需要设置联合主键,可以在多个值之间用逗号隔开. |
keyColumn | 此属性用于设置第几列时主键,当主键列不是表中的第一列时需要设置。 在需要主键联合时,值可以用逗号隔开. |
useGeneratedKeys | 此属性会使MyBatis使用JDBC的getGeneratedKeys() 方法来获取由数据库内部生产的主键,如MySQL和SQL Server等自动递增的字段,其默认值为false. |
执行插入操作后,很多场景都需要返回插入成功的数据库生成的主键值,需要使用上面的三个属性来实现
对于支持主键自动增长的数据库,可以通过如下配置实现:
<insert id="addCustomer" parameterType="Customer" useGeneratedKeys="true" keyProperty="id">
insert语句
</insert>
对于不支持主键自动增长的数据库,可以使用下面配置
<insert id="insertCustomer" parameterType="Customer">
<selectKey keyProperty="id" resultType="Integer" order="BEFORE">
select if(max(id) is null,1,max(id)+1) as newId from 插入表
</selectKey>
insert语句
</insert>
sql元素
其作用就是定义可重用的SQL代码片段,然后在其他语句中引用这一代码片段.
<!--定义查询的列-->
<sql id="columns">id,username,jobs,phone</sql>
<!--根据id查询用户信息-->
<select id="findCustomerById" parameterType="Customer" resultType="Customer">
select
<include refid="columns"/>
from t_customer where id=#{id}
</select>
resultMap元素表示结果映射集 , 它主要的作用是定义映射规则 , 级联的更新一级定义类型转化器等.
<resultMap type="需要封装到的类的全限定类名" id="唯一标识">
<constructor><!--类在实例化时,用来注入结果到构造方法中-->
<idArg/><!--ID参数;标记结果作为ID-->
<arg/><!--注入到构造方法的一个普通结果-->
</constructor>
<id/><!--用于表示哪个列是主键-->
<result/><!--注入到字段或JavaBean属性的普通结果-->
<association property=""/><!--用于一对一关联-->
<collection property=""/><!--用于一对多关联-->
<discriminator javaType=""><!--使用结果值来决定使用跟哪个结果映射-->
<case value=""/><!--基于某些值的结果映射-->
</discriminator>
</resultMap>
4.动态SQL
元素是最常用过的判断语句,它类似于Java中的if语句,主要用于实现某些简单的条件选择。
案例:根据性别和名称组合查询
<select id="findUserByNameAndSex" resultMap="userResultMap" parameterType="User">
select * from user where 1=1
<if test="userSex!='' and userSex!=null">
and sex=#{userSex}
</if>
<if test="userName!='' and userName!=null">
and username like concat('%',#{userName},'%')
</if>
</select>
<choose>
元素用于从多个where后的条件中仅选取一个符合条件的,类似于java中的switch语句
案例:根据姓名模糊查询用户,如果姓名为空查询年龄不为空的用户,如果年龄为空查询id不为空的客户
<!--choose元素用于从多个where后的条件中仅选取一个符合条件的-->
<select id="findUserByNameOrAgeOrId" parameterType="User" resultMap="users">
select * from t_user where 1=1
<choose>
<when test="name!='' and name!=null">
and t_name like concat('%',#{name},'%')
</when>
<when test="age!='' and age!=null">
and t_age=#{age}
</when>
<otherwise>
and t_id is not null
</otherwise>
</choose>
</select>
由于组装sql时,这个where 1=1并不怎么美观,这里我们就会用到<where>
元素,<trim>
元素
<where>
元素会自动判断SQL语句,只有<where>
内的条件成立时,才会在拼接SQL中加入where
关键字,否则不会添加;并且可以自动去除多余的and或or
<select id="findUserByNameAndId" parameterType="User" resultMap="users">
select * from t_user
<!--where元素和trim元素的作用是等价的-->
<where>
<if test="name!=null and name!=''">
and t_name like concat('%',#{name},'%')
</if>
<if test="age!=null and age!=''">
and t_age=#{age}
</if>
</where>
</select>
<trim>
的作用是去除特殊的字符串,他的prefix属性代表语句的前缀,prefixOverrides属性代表需要去除的哪些特殊字符串,功能和<where>
基本是等效的
<select id="findUserByNameOrAgeOrId" parameterType="User" resultMap="users">
select * from t_user
<trim prefix="where" prefixOverrides="and">
<choose>
<when test="name!='' and name!=null">
and t_name like concat('%',#{name},'%')
</when>
<when test="age!='' and age!=null">
and t_age=#{age}
</when>
<otherwise>
and t_id is not null
</otherwise>
</choose>
</trim>
</select>
<set>
元素在更新语句中出现,在hibernate中,如果想要更新某个对象,就需要发送所有的字段给持久化对象,这种想更新的每一条数据都要将其所有的属性都更新一遍的方法,其执行效率是非常差的,Mybatis中可以使用动态SQL中的set元素进行处理
<update id="updateUser" parameterType="User">
update t_user
<!--set元素会自动去除最后一段sql的逗号-->
<set>
<if test="age!='' and age!=null">
t_age=#{age},
</if>
<if test="name!='' and name!=null">
t_name=#{name},
</if>
</set>
where t_id=#{id}
</update>
<foreach>
元素使用时接在in后面,主要用来遍历集合或者数组
item:配置的是循环中当前的元素(如java实体类的属性id)
index:配置的是当前元素在集合的位置下标
collection:配置的list是传递过来的参数类型(首字母小写!),他可以是一个array,list(或collection)、Map集合的键、POJO包装类中的数组或集合类型的属性名等。
open和close:配置的是以什么符号将这些集合元素包装起来。
separator:配置的是各个元素的间隔符。
<select id="findUserByIds" parameterType="List" resultMap="users">
select * from t_user where t_id in
<foreach collection="list" item="id" index="i" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<bind>
元素可以通过OGNL表达式来创建一个上下文对象,用来解决字符串拼接问题
如果使用${}
进行字符串拼接,则无法防止SQL注入问题;
如果使用concat()
函数进行字符串拼接,则只针对MySQL数据库有效;
如果使用||
进行字符串拼接,则只针对Oracle
数据库有效。
没有一个统一的形式用于字符串拼接,不利于项目的移植mybatis提供了<bind>
元素来解决这一问题
<select id="findUserByNameBind" parameterType="user" resultMap="users">
<!--_parameter.getName()可以简写成name-->
<bind name="p_name" value="'%'+name+'%'"/>
select * from t_user where t_name like #{p_name}
</select>
三、第二天
实际开发中,对数据库的操作经常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好的处理对象与对象之间的关联关系。
在关系型数据库中,多表之间存在三种关联关系,分别为一对一,一对多,多对多
一对一:在任意一方引入对方的主键作为外键; 一对多:在多的一方,添加“一”的一方的主键作为外键; 多对多:使用中间表,引入两张表的主键作为外键,两个键成为联合主键或使用新的字段作为主键。
在Java中,通过对象也可以进行关联关系描述
一对一:在本类中定义对方类型的对象,如A类中定义B类类型的属性b,B类中定义A类类型的属性a; 一对多:一个A类类型对应多个B类类型的情况,需要在A类中以集合的方式引入B类类型的对象,在B类中定义A类类型的属性a; 多对多:在A类中定义B类类型的集合,在B类中定义A类类型的集合
一对一关联映射
mybatis通过<association>
元素处理一对一关系
javaType
:指定映射到实体类对象属性的类型
property
:指定映射到的实体类对象属性,与表字段一一对应
column
:指定表中对应的字段
select
:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询
fetchType
:指定在关联查询时是否启用延迟加载。该属性有lazy和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)
MyBatis加载关联关系对象主要通过两种方式:嵌套查询
和嵌套结果
。
嵌套查询时通过执行另外一条SQL映射语句来返回预期的复杂类型。
嵌套查询实在查询SQL中嵌入一个子查询SQL;
嵌套查询会执行多条SQL语句;
嵌套查询SQL语句编写较为简单;
嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集
嵌套结果是一个嵌套的多表查询SQL;
嵌套结果只会执行一条复杂的SQL语句;
嵌套结果SQL语句编写比较复杂;
使用嵌套查询的sql比较简单,但是嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大地消耗数据库性能并且会降低查询效率。
使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件中<settings>
元素内进行配置,具体配置方式如下:
<settings>
<setting name="lazyLoadingEnabled" value="true" /><!--延迟加载-->
<setting name="aggressiveLazyLoading" value="false" /><!--按需加载-->
</settings>
在映射文件中<association>
元素默认配置了延迟加载,在配置文件中开启延迟加载后就不需要再映射文件中再做配置
使用嵌套查询
**注意<association>
元素中的select
属性指向另一条SQL语句,column
属性要写你传入另一条sql时需要的参数的列名,比如另一条语句是用id
来进行查找,你这里就要使用对应的id
所在的列,另一张表的id
对应此表的card_id
,所以column
处填card_id
,**看不懂不要紧,这个嵌套查询没有嵌套结果好用,性能也是后者高
<!--嵌套查询-->
<resultMap id="PersonMap" type="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<!--一对一关联映射-->
<association property="card" column="card_id" javaType="IdCard" select="com.ligong.dao.IdCardMapper.findCodeById">
</association>
</resultMap>
<select id="findPersonById" parameterType="Integer" resultMap="PersonMap">
select * from tb_person where id=#{id}
</select>
使用嵌套结果,只执行一条sql语句效率高,sql语句复杂
<!--使用嵌套结果-->
<select id="findPersonById2" parameterType="Integer" resultMap="PersonMap2">
select p.*,i.code from tb_person p,tb_idcard i where p.card_id=i.id and p.id=#{id}
</select>
<resultMap id="PersonMap2" 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="card_id"/>
<result property="code" column="code"/>
</association>
</resultMap>
一对多关联映射
一个用户可以有多个订单,多个订单归一个用户所有。(一对多/多对一)
<resultMap>
元素中包含了一个<collection>
子元素,MyBatis就是通过该元素来处理一对多关联关系的。
<collection>
子元素的属性大部分与<association>
元素相同,但其还包括一个特殊属性<ofType>
<ofType>
属性与<javaType>
属性相近,用于指定实体类属性集合中包含的元素类型
<resultMap id="UserWithOrderMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<!--一对多关联映射-->
<collection property="orders" ofType="Order">
<id property="id" column="oid"/>
<result property="number" column="number"/>
</collection>
</resultMap>
<select id="findUserWithOrdersById" parameterType="Integer" resultMap="UserWithOrderMap">
select u.*,o.id as oid,o.number from tb_user u,tb_orders o where u.id=o.user_id and u.id=#{id}
</select>
多对多关联映射
需要在每个类中引入对方类型的集合
嵌套查询方式
这里依然要说明一下<collection>
元素中的column
属性,比如 “另一个SQL” 中他的条件需要一个orders_id=#{id}
<!--这里是“另一条SQL”-->
<mapper namespace="com.ligong.dao.ProductMapper">
<select id="findProductById" parameterType="Integer" resultType="Product">
select * from tb_product where id in(
<!--需要个能和orders_id对应的参数-->
select product_id from tb_ordersitem where orders_id=#{id}
)
</select>
</mapper>
orders_id该字段所在的表为中间关联表,其关联tb_orders表的id字段,所以下面的column处需要写id
<!--多对多嵌套查询-->
<resultMap id="OrderByIdMap" type="Order">
<id property="id" column="id"/>
<result property="number" column="number"/>
<!--tb_orders表的主键(id)与关联表tb_ordersitem的外键(orders_id)关联,所以这里填写id-->
<collection property="productList" column="id" ofType="Product" select="com.ligong.dao.ProductMapper.findProductById" />
</resultMap>
<select id="findOrderWithProductById" parameterType="Integer" resultMap="OrderByIdMap">
select * from tb_orders where id=#{id}
</select>
多对多嵌套结果(推荐使用的,只执行一条SQL语句,效率高)
<!--多对多嵌套结果-->
<select id="findOrderWithProductById2" parameterType="Integer" resultMap="OrderWithProductById2">
select o.*,p.id as pid,p.name,p.price from tb_orders o,tb_product p,tb_ordersitem i where i.orders_id=o.id
and i.product_id=p.id and o.id=#{id}
</select>
<resultMap id="OrderWithProductById2" type="Order">
<id property="id" column="id"/>
<result property="number" column="number"/>
<collection property="productList" ofType="Product">
<id property="id" column="pid"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
</collection>
</resultMap>
三、第三天
注解的使用
mybatis注解方式和xml映射文件方式只能使用一种,当使用注解方式时就不能使用xml的方式了,会报错
使用注解需要在mappers元素中注册目标接口的位置
查询所有用户信息,添加用户信息(增删改查对应insert delete update select)
@Select("select * from user")
List<User> findAll();
@Insert("insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
void saveUser(User user);
@Results注解:该注解必须作用于需要配置映射关系的方法之上,有两个常用属性如下
String id() default “”; id为唯一标识符,可以被引用
Result[] value() default {}; 可以定义多个@Result注解的数组
@Results(id = "userResultMap",value = {
@Result(id = true,property = "userId",column = "id"),
@Result(property = "userName",column = "username"),
@Result(property = "userBirthday",column = "birthday"),
@Result(property = "userSex",column = "sex"),
@Result(property = "userAddress",column = "address")
})
@Select("select * from user")
List<User2> findAllUser2s();
@ResultMap注解:引用映射关系
@Select("select * from user where id=#{userId}")
@ResultMap("userResultMap")
User2 findUser2ById(Integer userId);
@Result注解:
public @interface Result {
boolean id() default false; //用于标识主键,默认为false
String column() default "";
String property() default "";
Class<?> javaType() default void.class;
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
One one() default @One;
Many many() default @Many;
}
//用于配置一对一关系
public @interface One {
String columnPrefix() default ""; //
String resultMap() default "";
String select() default "";
FetchType fetchType() default FetchType.DEFAULT;
}
//用于配置一对多关系或多对多关系
public @interface Many {
String columnPrefix() default "";
String resultMap() default "";
String select() default "";
FetchType fetchType() default FetchType.DEFAULT;
}
public enum FetchType {
LAZY,//延迟加载
EAGER,//立即加载
DEFAULT;
}
@Select("select * from account")
@Results(id="accountMap",value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
})
List<Account> findAll();
@CacheNamespace(blocking=true) 默认为false,true为启动二级缓存