文章目录
- mybatis
- 第一章
- 1、什么是mybatis
- 2、idea中配置环境
- 3、创建一个普通工程
- 第二章
- 1、mybatis基本步骤
- 2、导入log4j日志
- 3、使用lombok注解
- 4、mapper.xml文件详情
- 1、parameterType属性
- 2、resultType属性
- 5、对实体包进行扫描
- 6、SQL语句中的占位符及转义符
- 7、接口方法包含多个参数
- 8、resultMap手动封装
- 1、手动封装解决
- 2、起别名解决
- 3、resultMap与resultType的区别
- 9、查询
- 1、按成绩范围查询
- 2、区间查询
- 3、模糊查询
- 4、多条件查询(不确定有几个查询条件)
- 5、条件查询的参数接受方式
- **散装参数的接收**
- **对象参数的接收**
- **集合参数的接收**
- 6、分页查询
- 10、动态sql查询
- 11、添加
- 12、修改
- 1、修改全部字段
- 2、动态修改字段
- 13、删除
- 1、删除一个
- 2、批量删除
- 第三章
- 1、基本语法
- 1、代码片段
- 2、返回类型
- 3、插件
- 1、使用步骤
- 4、mybatis的关联查询
- 1、一对多以及多对一
- 实体类
- **注意**
- 1、多对一的关联查询
- 2、一对多的关联查询
- 3、延迟加载
mybatis
第一章
1、什么是mybatis
mybatis是一个持久层框架。它主要是用于简化数据库的操作
持久层:与数据库交互的层次就叫持久层
前身:在ibatis框架上产生的新框架
开发时比较流行的一种框架组合方式:
ssm:(spring+springmvc+mybatis)
mybatis必须要与spring配合使用才能变的更简单
//单独使用会很麻烦:
2、idea中配置环境
1、在idea中配置mybatis的mapper.xml文件与mybatis-config.xml文件
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>
<!-- 引用属性文件 -->
<properties resource=""/>
<!-- 配置别名 -->
<typeAliases>
</typeAliases>
<!-- 配置mybatis的运行环境 -->
<environments default="development">
<environment id="development">
<!-- 采用的事务方式 -->
<transactionManager type="jdbc" />
<!-- 配置数据源信息 -->
<dataSource type="pooled">
<property name="url" value="${url}" />
<property name="driver" value="${driver}" />
<property name="username" value="${user}" />
<property name="password" value="${pwd}" />
</dataSource>
</environment>
</environments>
<!-- 注册映射文件 -->
<mappers>
<mapper resource=""/>
</mappers>
</configuration>
3、创建一个普通工程
1、创建普通工程
2、在main目录下,新增resources目录,用于存放配置文件(*.xml,*.properties)
3、导入依赖
1、mybatis---->3.3.1
2、mysql-connector-java----->5.1.47--->scope:runtime
4、在resources导入一个jdbc.properties,用于指定连接的数据库
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/ajaxinfo? useUnicode=true&characterEncoding=UTF-8
user=root
pwd=123456
5、创建resources目录下创建mybatis的主配置文件:mybatis-config.xml
添数据时用el表达式
${url}
${driver}
...
6、修改mybatis-config.xml文件
<!-- 引用属性文件 -->
<properties resource="jdbc.properties"/>
7、编写实体类与数据库结构对应
stuInfo.java
8、编写数据访问层dao接口
命名规范:xxxmapper(以前以dao结尾,现在以mapper结尾)
userMapper
//数据访问层接口
public interface userMapper {
public void add(userInfo user);
}
9、创建resources目录下创建mapper.xml文件,用于封装对stu表操作的所有sql语句
//此处有坑
要求:1、mapper.xml文件必须放在resources目录下
2、mapper.xml的目录结构必须要与stuMapper接口的目录名一致
//坑(不能一步创建,必须分开创建包org->java->dao)
3、接口名必须与xml名保持一致,只是后缀名不同而已
接口:stuMapper.java
配置文件名:stuMapper.xml
4、mapper.xml文件中的namespace(命名空间)必须是mapper接口的全路径
全路径:包名加接口名
5、采用对应的标签封装对应的sql语句
insert,update,delete,select
6、语句的id名称必须与mapper接口中的方法名同名,并且参数名称也要同名
<mapper namespace="org.java.dao.userMapper">
<!--添加-->
<insert id="add" parameterType="org.java.enty.userInfo">
insert into user values(null,#{name},#{pwd},null)
</insert>
</mapper>
10、在mybatis-config.xml文件中扫描所有接口
<mappers>
<!--扫描指定包下面的所有mapper接口-->
<package name="org.java.dao"></package>
</mappers>
11、在org.java.util包中,编写MyBatisUtil类,用于连接数据库
public class MyBatisUtil {
//用于执行对数据库的所有操作(相当于jdbc中的connection)
private static SqlSession sqlSession;
//编写静态块,初始sqlsession
static {
try {
//创建输入流,用于读取主配置文件mybatis-config.xml
InputStream in= Resources.getResourceAsStream("mybatis_config.xml");
//创建一个SqlSessionFactoryBuilder构建器
SqlSessionFactoryBuilder db=new SqlSessionFactoryBuilder();
//创建SqlSessionFactory用于产生sqlsession
SqlSessionFactory factory=db.build(in);
//产生sqlsesion
sqlSession=factory.openSession();
//关闭流
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSession;
}
}
12、编写业务层service
userService---它负责调用mapper接口的方法实现具体的操作
public class userService {
public void add(userInfo user){
//获得sqlSession
SqlSession sqlSession= MyBatisUtil.getSqlSession();
//通过sqlsession获得mapper接口
userMapper mapper=sqlSession.getMapper(userMapper.class);
//通过mapper接口对数据库进行操作
mapper.add(user);
//如果是增删改操作,必须要进行事务提交(commit)
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
}
}
13、编写测试类demo
public class userDemo {
userService service=new userService();
public static void main(String[] args) {
new userDemo().add();
}
private void add() {
//创建一个用户对象
userInfo user=new userInfo();
user.setName("lwj");
user.setPwd("123");
//执行操作
service.add(user);
}
}
不能直接org.java.dao创建
//必须:org–>java–>dao分开创建
//另外:org/java/dao这样创建也可以
第二章
1、mybatis基本步骤
1、创建工程
2、导入依赖
3、导入db.properties
4、编写主配置文件mybatis-config.xml
5、编写实体类
6、编写Mapper接口
命名规范是:实体类的名称Mapper
例如:InfMapper
7、在resources目录下,编写Mapper.xml文件,封装对inf表操作的各种sql
特别注意:InfMapper.xml文件的配置必须放在resources目录下,并且要与 InfMapper接口所在包名一致,而且要注意,包的目录
要分别创建,不能一起创建
8、在mybatais-config.xml文件配置扫描,扫描所有的mapper接口
<mappers>
<package name="org.java.dao"/>
</mappers>
9、编写MyBatisUtil工具类
@@@注意:我们在编写工具类时,使用sqlSession时没有提示的原因是因为idea12021的版本在pom.xml文件中导入依赖后,需要
@@@手动刷新才能加入依赖
10、编写InfService业务类,用于执行各种增删改查
11、编写Demo测试类
2、导入log4j日志
@@@@每一次对mybatis操作后,控制台没有提示,我们看不到底层执行的sql语句,调试错误不方便,为了解决该问题,我们可以导入
@@@log4j日志
解决办法:导入log4j日志,对数据库的每一个操作,都会显示对应的sql语句,方便调试错误
1、导入2个依赖
1、log4j
2、cglib-nodep
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
</dependency>
2、将log4j.properties日志文件导入到resources目录下即可
3、使用lombok注解
lombok的作用:
简化实体类,自动生成get,set, toString,equals这些方法,也可以生成带参数以及不带参数的构造方法,让实体类的可读性更好
使用步骤:
1、导入lombok的依赖
<!-- 导入lombok的依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
2、在实体类中使用注解生成对应的方法
@Data-------该注解自动生成set,get,toString,equals方法
@AllArgsConstructor---------自动生成带参数的构造方法
@NoArgsConstructor----------自动生成无参的构造方法
@Getter---------------------生成get方法
@Setter---------------------生成set方法
@@@@注意:要使用lombok注解时,需要在idea中安装一个lombok的插件
4、mapper.xml文件详情
1、parameterType属性
parameterType:它用于指定参数类型
情况1:如果参数是一个实体对象,占位符的名称必须是实体对象中的某一个属性
@@@ #{xxx} 它用于指定参数占位符
<insert id="add" parameterType="org.java.entity.Inf">
insert into inf values(null,#{name},#{score})
</insert>
情况2:如果参数是一个具体的数据类型,占位符的名称可以任意指定(但建议尽量与属性名一致,增强代码的可读性)
<delete id="del" parameterType="int">
delete from inf where id=#{id}
</delete>
情况3:如果参数是一个Map集合,占位符的名称需要与map中的键对应即可
<insert id="add2" parameterType="map">
insert into inf values(null,#{myname},#{myscore})
</insert>
parameterType="map"--------此代码,表示参数是一个Map集合
parameterType:可以省略
2、resultType属性
resultType:它用于指定查询到的【一条数据】应该封装成什么数据类型进行返回
最终是返回一个对象还是一个集合并不是由它决定,而是由查询到的数据总数决定
它只是负责指定查询到的每一条记录,应该封装成什么类型
<select id="findAll" resultType="org.java.entity.Inf">
select * from inf
</select>
查询全部数据,每一条数据封装成一个Inf对象,放入到List集合中
<select id="findById" parameterType="int" resultType="org.java.entity.Inf">
select * from inf where id=#{id}
</select>
根据条件,查询一条数据,将这一条数据转换成Inf对象返回
手动封装类型
<!--采取映射的方式,来解决数据表与属性名不同导致取不导致的问题
id:唯一标识
type:映射的类型
-->
<resultMap id="userResultMap" type="userInfo">
<!--column:数据库的字段名;property:实体类的属性名-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
<result column="regtime" property="registerTime"/>
</resultMap>
********************使用
<!--查询 使用resultMap属性替换之前的resultType属性-->
<select id="selectAll" resultMap="userResultMap">
select * from user
</select>
5、对实体包进行扫描
@@@@默认情况下,mapper.xml文件如果参数是一个实体对象或者是返回类型是一个实体对象,都要指定实体类的全路径,但这样太繁琐
解决方案:
在mybatis-config.xml的typeAliases部分中,对实体类所在的包进行扫描,指定别名即可
<typeAliases>
<package name="org.java.enty"/>
</typeAliases>
此代码用于对当前org.java.enty包中的实体类生成别名,名称为类名的首字母小写
例如:类名:Inf
别名:inf
//设置此项配置后,就不用输入全路径了
配置之前: <select resultType="org.java.entity.Inf"></select>
配置之后: <select resultType="Inf"></select>
6、SQL语句中的占位符及转义符
<!--根据id查询-->
<!--参数占位符:
1、#{}:会将其替换为 ?,为了防止SQL注入
2、${}:拼sql,会存在SQL注入问题
3、使用时机:
参数传递的时候:#{}
表名或者列名不固定的情况下:${}
参数类型:
parameterType:可以省略、
特殊字符:
1、转义字符:< == < 字符少使用
2、CDATA区:<![CDATA[ 字符多使用
<
]]>
-->
<select id="selectId" parameterType="int" resultMap="userResultMap">
select * from user where id=#{id}
</select>
7、接口方法包含多个参数
如果要接口的方法要包含多个参数,就需要采用注解,并且在mapper.xml不需要指定parameterType属性,语句中的占位符的名称
必须是注解参数的别名
public void add3(@Param("name") String name,@Param("score") int score);
<insert id="add3">
insert into inf values(null,#{name},#{score})
</insert>
8、resultMap手动封装
如果要将查询到的一条记录封装成一个具体的实体对象,要求:数据表中的字段必须要与类中属性名一致才可以
@@@@@@@@resultType:这种方式是自动封装返回类型
它用于指定查询到的一条记录要封装成什么数据类型
这种方式要求:数据表中查询出来的字段名必须要与类中的属性名一致才能封装
@@@@@@@@resultMap:这种方式用于手动封装返回类型
它用于指定查询到的一条记录要封装成什么数据类型
这种方式必须要自己指定将哪一个字段赋值给类中的哪一个属性(即使属性名不一致,也是可以封装的)
【问题:字段名与属性名不一致要封装对象,两种解决办法】
1、手动封装解决
方案1:用resultMap手动封装返回类型
<resultMap id="infMap" type="inf">
<id column="tb_id" property="id"/>
<result column="tb_name" property="name"/>
<result column="tb_score" property="score"/>
</resultMap>
<select id="findAll2" resultMap="infMap">
select * from inf2
</select>
2、起别名解决
方式2:给字段名指定别名即可(更简单)
<select id="findAll2" resultType="inf">
select tb_id id,tb_name name,tb_score score from inf2
</select>
3、resultMap与resultType的区别
答:使用resultType是自动封装返回类型,但这种方式无法封装多表关联查询后产生的关联属性
使用resultMap是手动封装返回类型,这种方式可以封装多表关联查询后产生的关联属性
@@@如果是封装单表查询结果,没有关联属性时,一般使用resultType
@@@如果是封装多表查询结果,有关联属性时,一般使用resultMap
9、查询
1、按成绩范围查询
<select id="findByScore" resultType="inf" parameterType="int">
select * from inf where score>=#{score} order by score desc
</select>
查询分数大于等于指定分数的学生
【问题:部分符号要转义】
注意:在xml文件中,<符号不能直接作为运算符使用,系统会把它当作一个标签的开始
小于符号必须要转义才能使用
< <
<= <=
<select id="findByScore" resultType="inf" parameterType="int">
select * from inf where score <=#{score} order by score desc
</select>
2、区间查询
<select id="findByScoreBetween" resultType="inf">
select * from inf where score between #{begin} and #{end} order by score desc
</select>
3、模糊查询
<select id="findByName" parameterType="String" resultType="inf">
select * from inf where name like concat('%',#{name},'%')
</select>
4、多条件查询(不确定有几个查询条件)
<select id="findByCondition" parameterType="inf" resultType="inf">
select * from inf
<where>
<if test="id!=null">
and id=#{id}
</if>
<if test="name!=null and name!=''">
and name like concat('%',#{name},'%')
</if>
<if test="score!=null">
and score>=#{score}
</if>
</where>
order by score desc
</select>
5、条件查询的参数接受方式
/*条件查询--参数接受
1、散装参数:如果方法中有多个参数,需要使用@Param(“SQL参数占位符名称)
2、对象参数
3、map集合参数
*/
//1、散装参数
List<userInfo> selectCondition(@Param("id") int id,@Param("name") String name);
//2、对象参数
List<userInfo> selectCondition2(userInfo user);
//3、map参数参数
List<userInfo> selectCondition3(Map map);
散装参数的接收
private void selectCondition(int id,String name){//多条件查询
//1、采用注解的方式多条件查询
//处理参数,因为是模糊查询,需要将字符串两边加上%号
String resteName="%"+name+"%";
//执行sql语句并输出
System.out.println(service.selectCondition(id,resteName));
}
对象参数的接收
private void selectCondition2(int id,String name){//多条件查询
//2、封装对象来多条件查询
//处理参数,因为是模糊查询,需要将字符串两边加上%号
String resteName="%"+name+"%";
//封装对象
userInfo user=new userInfo();
user.setId(id);
user.setName(resteName);
//执行sql语句并输出
System.out.println(service.selectCondition2(user));
}
集合参数的接收
private void selectCondition3(String name){//多条件查询
//3、封装集合来多条件查询
String resteName="%"+name+"%";
//封装集合
Map map=new HashMap();
// map.put("id",id);
map.put("name",resteName);
//执行sql语句并输出
for (userInfo userInfo : service.selectCondition3(map)) {
System.out.println(userInfo);
}
}
6、分页查询
<!-- 查询总条数-->
<select id="getCount" resultType="integer">
select count(*) from user
</select>
<!-- 分页查询-->
<select id="findPage" resultMap="userResultMap">
select * from USER limit #{startIndex},#{rows}
</select>
=======================传参数
List<userInfo> findPage(@Param("startIndex") int startIndex,@Param("rows") int rows);
注解的意思就是:指定在SQL语句中参数名的值
10、动态sql查询
@SelectProvider(type= sqlutils.class,method = "rolequery")
<!--多条件查询 动态sql
*if:条件判断
test:逻辑表达式
*问题:
where判断时有的参数有值,有的参数没有值,就会产生sql语句报错
*解决:
1、恒等式 where 1=1
2、<where> 替换 where关键字
-->
示例:
//用<where> 替换 where关键字
<select id="selectCondition" resultMap="userResultMap">
select * from user
<where>
<if test="id!=null">
and id=#{id}
</if>
<if test="name!=null and name!=''">
and name like #{name}
</if>
</where>
</select>
单条件查询
11、添加
<!--添加-->
<insert id="add" parameterType="userInfo">
insert into user values(null,#{name},#{pwd},null)
</insert>
主键返回
//添加两个属性之后,就可以获得刚刚添加到数据库的主键id
useGeneratedKeys="true"
keyProperty="id"
获得主键id
private void add() {//添加
//创建一个用户对象
userInfo user=new userInfo();
user.setName("bulus");
user.setPwd("111");
//执行操作
service.add(user);
//获得刚刚添加的主键id
System.out.println(user.getId());
}
12、修改
1、修改全部字段
userMapper.xml文件
<!--修改 此时是修改全部字段-->
<update id="update" parameterType="userInfo">
update user
set name=#{name},
pwd=#{pwd}
where id=#{id}
</update>
编写接口userService.java
int update(userInfo user);//修改
编写userService.java
public Integer update(userInfo user) {
int count=mapper.update(user);
//如果是增删改操作,必须要进行事务提交(commit)
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
return count;
}
执行操作
private void update(int id,String name,String pwd) {//修改
//创建一个用户对象
userInfo user=new userInfo();
user.setName(name);
user.setPwd(pwd);
user.setId(id);
//执行操作
System.out.println(service.update(user));
}
2、动态修改字段
//只需要修改sql语句
<update id="update" parameterType="userInfo">
update user
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="pwd!=null and pwd!=''">
pwd=#{pwd},
</if>
</set>
where id=#{id}
</update>
13、删除
1、删除一个
<!--删除-->
<delete id="del" >
DELETE FROM user WHERE id = #{id}
</delete>
2、批量删除
<!--批量删除 如果不使用注解 ,那么collection=“array”-->
<delete id="delIds">
DELETE FROM user WHERE id
in
<foreach collection="ids" item="id" separator="," open="(" close=" )">
#{id}
</foreach>
</delete>
编写接口
//使用注解,ids代表传进来的数组
void delIds(@Param("ids") int[] ids);//批量删除
编写service
public void delIds(int[] ids) {
mapper.delIds(ids);
//如果是增删改操作,必须要进行事务提交(commit)
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
}
执行操作
private void delIds(){//批量删除
int[] ids={3,4,7};
service.delIds(ids);
}
第三章
1、mybatis基本语法
2、mybatis的关联查询
1、基本语法
1、代码片段
在编写mapper.xml文件时,公共代码可以抽取出来,形成一个代码片段,避免出现重复代码
<!-- 代码片段-->
<sql id="ConditionOne">
<where>
<if test="id!=null and id!=0">
and id=#{id}
</if>
<if test="name!=null and name!=''">
and name like #{name}
</if>
</where>
</sql>
使用片段********************************
select count(*) from user
<include refid="ConditionOne"></include>
2、返回类型
查询的一条数据可以封装成一个实体对象返回,也可以封装一个map集合返回
**一般在单表操作时,查询到的一条记录封装成实体类返回,可读性更好
**一般在多表连接时,可以把查询到的一条记录封装成map返回,更方便
示例:联表查询返回map集合
user表:存储用户名和密码
particulars表:存放用户详细信息
xml语句
<!-- 根据id进行多表查询-->
<select id="findById" resultType="map">
select a.name,a.pwd,b.gender,b.age,b.tel,b.email,b.address from user a,particulars b where a.id=b.uid and a.id=#{id}
</select>
返回一个map对象
3、插件
在刚才的练习中,为了对user表进行操作,我们进行了如下操作
1、编写了user类,自己编写了类中的属性
2、编写了userMapper接口,自己编写了接口的方法
3、编写了userMapper.xml,自己编写了Mapper.xml文件中的语句
【问题】:如果有100张表,我们还有一一创建吗?
【解决】:我们一般使用mybatis插件直接生成代码
1、使用步骤
1、准备mybatis插件的配置文件及jar
2、修改generatorConfig.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动-->
<classPathEntry location="mysql-connector-java-5.1.37-bin.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库链接URL,用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/ajaxinfo" userId="root" password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成模型entity的包名和位置-->
<javaModelGenerator targetPackage="org.java.entity" targetProject="F:\D118MavenFile\Item\maven_02\src\main\java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成映射文件的包名和位置-->
<sqlMapGenerator targetPackage="org.java.dao" targetProject="F:\D118MavenFile\Item\maven_02\src\main\resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成DAO的包名和位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="org.java.dao" targetProject="F:\D118MavenFile\Item\maven_02\src\main\java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
<table tableName="%" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
3、通过cmd进入当前目录,执行下列命令生成代码
java -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite
4、mybatis的关联查询
数据库中的表如果存在关联,通过mybatis可以进行关联查询
mybatis中的关联结构主要有四种:
1、一对多
ag:省份与城市-----一个省份包含多个城市
2、多对一
ag:多个城市隶属于同一个省份
3、一对一
ag:人员与身份证----一个人对应一个身份证
4、多对多
ag:学生与老师----一个老师可以教多个学生
一个学生也可以被多个老师教
通过mybatis的关联查询,可以在查询一个对象时,同时将与它相关联的对象同时查询出来
1、一对多以及多对一
一般如果数据表之间存在主外键关联,就可以配置成一对多以及多对一的关联
【省份表】(主键表)
CREATE TABLE province
(
pid INT PRIMARY KEY AUTO_INCREMENT, #省份编号
pname VARCHAR(20) #省份名称
);
INSERT INTO province VALUES(1,'湖北省');
INSERT INTO province VALUES(2,'广东省');
【城市表】(外键表)
CREATE TABLE city
(
cid INT PRIMARY KEY AUTO_INCREMENT,#城市编号
cname VARCHAR(20),#城市名称
pid INT,#城市所属省份编号
FOREIGN KEY (pid) REFERENCES province(pid) #外键,关联到省份表的主键
)
INSERT INTO city VALUES(1,'武汉市',1);
INSERT INTO city VALUES(2,'襄阳市',1);
INSERT INTO city VALUES(3,'广州市',2);
SELECT * FROM province;
SELECT * FROM city;
实体类
如果两个实体对象之间有关联关系:
在一的一边,将增加一个集合类型的关联属性,集合中放所有的外键对象
在多的一边,外键字段将会变成它对应的主键表所对应的实体类型
province表
pid-------省份编号(主键)
pname-----省份名称
city表
cid--------城市编号(主键)
cname------城市名称
pid--------外键(关联省份)
Province类(一)
pid
pname
//关联属性
List<City> cites = new ArrayList<>();
City类(多)
cid
cname
//关联属性
Province province;
如果希望在java目录中使用junit,只需要改个范围
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>compile</scope>
</dependency>
注意
在执行关联查询时要注意
resultType:用于自动封装返回类型,只能查询到数据表中有对应的属性,无法查询关联属性
resultMap:用于手动封装返回类型,这种方式可以加载相关属性
1、多对一的关联查询
查询城市时,同时查询出与它关联的省份
cityMapper.xml文件
*****************************二次查询
<!-- 编写二次查询,传递一个pid查询一个省份对象-->
<select id="findProvinceByPid" parameterType="int" resultType="province">
select * from province where pid=#{pid}
</select>
*****************************手动封装返回类型
<!-- 封装返回关联属性-->
<resultMap id="propResultMap" type="City">
<!-- 主键字段-->
<id column="cid" property="cid" />
<!-- 普通字段-->
<result column="pid" property="pid" />
<result column="cname" property="cname" />
<!-- 关联属性
多对一以及一对一的关联属性,使用association进行配置
一对多以及多对多的关联属性,使用collection进行配置
select="要查询的语句"
-->
<association property="province" column="pid" select="findProvinceByPid" />
</resultMap>
*********************************sql片段
<sql id="Base_Column_List" >
cid, pid, cname
</sql>
*********************************查询语句
<select id="selectByPrimaryKey" resultMap="propResultMap" >
select
<include refid="Base_Column_List" />
from city
where cid = #{cid,jdbcType=INTEGER}
</select>
2、一对多的关联查询
查询省份时,同时查询该省份管辖的所有城市
ProvinceMapper.xml文件
*****************************二次查询
<!-- 编写二次查询,传递一个pid查询一个省份对象-->
<select id="findByPid" resultType="city">
select * from city where pid=#{pid}
</select>
*****************************手动封装返回类型
<resultMap id="propResultMap" type="province">
<id column="pid" property="pid" />
<result column="pname" property="pname"/>
<!--关联属性-->
<collection property="list" column="pid" select="findByPid"/>
</resultMap>
*********************************sql片段
<sql id="Base_Column_List" >
pid, pname
</sql>
*********************************查询语句
<select id="selectByPrimaryKey" resultMap="propResultMap" >
select
<include refid="Base_Column_List" />
from province
where pid = #{pid}
</select>
默认情况下,在mybatis中如果查询了数据库,将会显示对应的sql语句
如果在配置文件中,配置了关联属性,在查询时,会默认查询两次数据库
【问题】假设有1个主贴,这个主贴有1000个回贴
现在我们只想看主贴时,由于配置了关联属性,当查询主贴时,系统会把这1000个回贴也查询出来,很浪费资源
【期望结果】查询主贴时,只查看主贴,需要用到回贴时再查询回贴,如果不查看回帖就不要进行二次查询
【解决方案】配置‘延迟加载’
指:关联属性一开始并不加载,什么时候用关联属性,什么时候就进行二次查询,不用就不查询
3、延迟加载
在mybatis-config.xml文件配置延迟加载
lazy:懒惰、延迟
<!-- 引用属性文件 -->
<properties resource="jdbc.properties"/>
************开始****************
<!--配置延迟加载机制-->
<settings>
<!--启用延迟加载机制-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--不使用积极加载(什么时候使用关联属性,什么时候查询关联表)-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
************结束****************
<!-- 配置别名 -->
<typeAliases>
<!--扫码实体层的所有实体类-->
<package name="org.java.entity"></package>
</typeAliases>