1.SSM框架介绍(Spring+SpringMVC+MyBatis)
Junit测试、log4j日志
2.MyBatis框架
1)MyBatis框架介绍(ORM对象关系映射)工作在三层架构持久层
2)MyBatis的第一个程序:student插入信息
MyBatis的主配置文件:连接数据库的四要素
映射文件:Sql语句
实现类:SqlSessionFactoryBuilder->SqlSessionFactory->SqlSession->执行sql命令
3)配置文件解析:定义别名、加载属性文件、注册映射文件、配置多个连接环境、使用日志、定义工具类
4)单表的增删改查(CURD)
5)mapper的动态代理实现增删改查
使用动态代理条件:
接口和对应映射文件在同一包下、映射文件的namespace应该是接口的全限定性类名、sqlSession.getMapper(映射文件对应接口.class)
6)动态SQL标签
、、、、sql片段
7)关联关系查询:1:n、n:1、1:1、n:m、自关联
多表连接查询、多表单独查询
的用法
8)字段名和属性名不一致:起别名、resultMap
9)延迟加载:关联关系中关联对象的多表单独查询能使用延迟加载策略
直接加载默认的
侵入式延迟加载
深度延迟加载
延迟加载的开关:主配置文件中
10)缓存
一级缓存和二级缓存(作用范围都是namespace)
一级缓存的生命周期和SQLSession相同
二级缓存的生命周期和整个应用的生命周期相同
一级缓存默认开启,无开关设置
二级缓存默认关闭,可以设置打开或关闭
二级缓存的全局开关在主配置文件中默认为true
映射文件中可以打开在该命名空间的二级缓存
在映射文件中的也可以单独设置使用不使用二级缓存,用useCache
11)MyBatis注解
注解的基本用法:@Insert、@Update、@Delete、@Select
使用动态Sql:@InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider
生成动态sql的方法:字符串拼接(StringBuffer)、SQL类(匿名内部类)
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
package com.wcc.beans;
public class Student {
private Integer id;
private String name;
private int age;
private double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
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;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
4.创建dao接口类
package com.wcc.dao;
import com.wcc.beans.Student;
public interface IStudentDao {
void insertStudent(Student student);
}
5.创建dao接口实现类
package com.wcc.dao;
import com.wcc.beans.Student;
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 java.io.IOException;
import java.io.InputStream;
/*
* 1.SqlSessionFactoryBuilder
* 2.SqlSessionFactory
* 3.SqlSession
* */
public class StudentDaoImpl implements IStudentDao {
@Override
public void insertStudent(Student student) {
SqlSession session = null;
try {
//1.创建SqlSessionFactoryBulider对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//2.加载主配置文件为InputStream类型
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//3.由builder创建SqlSessionFactory
SqlSessionFactory factory = builder.build(is);
//4.由factory创建SqlSession
session = factory.openSession();
//5.执行插入命令
session.insert("insertStu",student);
//6.提交
session.commit();
} catch (IOException e) {
e.printStackTrace();
}finally {
//7.关闭SqlSession
if (session != null){
session.close();
}
}
}
}
6.创建xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
//namespace为dao接口层的权限定性接口名
<mapper namespace="com.wcc.dao.IStudentDao">
//parameterType为POJO的权限定性类名
<insert id="insertStu" parameterType="com.wcc.beans.Student">
insert into student (name,age,score) values (#{
name},#{
age},#{
score})
</insert>
</mapper>
7.创建测试类
*/
public class MyTest
{
@Test
public void test()
{
IStudentDao studentDao = new StudentDaoImpl();
Student student = new Student("小明",20,98.5);
studentDao.insertStudent(student);
}
}
1.将sqlSessionFactory定义为单例模式,提前将Sqlsession创建出来。这样dao的实现类每次增删改查就不用在创建sqlSession了。
package com.yyf.utils;
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 java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
//因为SqlSessionFactory是重量级组件,把它定义为单例的对象
private static SqlSessionFactory factory = null;
public static SqlSession getSqlSession() throws IOException {
if (factory == null){
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.xml"));
}
return factory.openSession();
}
}
2.添加log2j日志,便于调试程序
1)在pow文件中引入log2j的依赖
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
2)java目录下编写log2j2.xml文件
```java
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="myConsole" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5p] %m%n" />
</Console>
</appenders>
<loggers>
<logger name="com.yyf.dao.IStudentDao" level="trace" additivity="false">
<appender-ref ref="myConsole" />
</logger>
<!--<root level="debug">
<appender-ref ref="myConsole" />
</root>-->
</loggers>
</configuration>
3.给主配置文件mybatis.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>
<!--加载jdbc属性文件-->
<properties resource="jdbc.properties"/>
<!--给实体类定义别名-->
<typeAliases>
<!--给指定的类定义指定的别名-->
<!--<typeAlias type="com.yyf.beans.Student" alias="Stu"/>-->
<!--给指定包下所有的类起别名,默认别名为该类的简单类名-->
<package name="com.yyf.beans"/>
</typeAliases>
<environments default="development">
<environment id="development">
<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>
<!--<environment id="oracle">
<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>
<!--注册映射文件-->
<!--
resource一般加载的是内部资源映射文件
-->
<!--<mapper resource="com/yyf/dao/mapper.xml"/>-->
<!--url一般加载项目的外部资源-->
<!--<mapper url=""/>-->
<!--
1.映射文件和对应的接口在同一个包下
2.映射文件的名称必须和接口的简单类名相同
3.映射文件中namespace的值必须是对应接口的全限定性类名
-->
<!--<mapper class="com.yyf.dao.IStudentDao"/>-->
<!--
1.映射文件和对应的接口在同一个包下
2.映射文件的名称必须和接口的简单类名相同
3.映射文件中namespace的值必须是对应接口的全限定性类名
4.(可以不满足)使用动态mapper
-->
<package name="com.yyf.dao"/>
</mappers>
</configuration>
4.将sqlSessionFactory定义为单例模式后的dao接口实现类
package com.yyf.dao;
import com.yyf.beans.Student;
import com.yyf.utils.MyBatisUtil;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
/*
* 1.SqlSessionFactoryBuilder
* 2.SqlSessionFactory
* 3.SqlSession
* */
public class StudentDaoImpl implements IStudentDao {
//插入学生信息
@Override
public void insertStudent(Student student) {
SqlSession session = null;
try {
//1.通过工具类获取SqlSession对象
session = MyBatisUtil.getSqlSession();
//2.执行插入命令
session.insert("insertStu",student);
//3.提交
session.commit();
} catch (IOException e) {
e.printStackTrace();
}finally {
//4.关闭SqlSession
if (session != null){
session.close();
}
}
}
使用Mapper的动态代理要求:
* 1.sqlSession.getMapper()中的参数为所要代理接口的类型
* 2.映射文件的namespace属性值必须为所代理接口的全限定性类名
* 3.映射文件中的statement的id必须和代理接口中的相应方法名称相同
1.测试类中直接调用sqlSession
package com.yyf;
import static javafx.scene.input.KeyCode.I;
import static org.junit.Assert.assertTrue;
import com.yyf.beans.Student;
import com.yyf.dao.IStudentDao;
import com.yyf.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyTest
{
/**
* 使用Mapper的动态代理要求:
* 1.sqlSession.getMapper()中的参数为所要代理接口的类型
* 2.映射文件的namespace属性值必须为所代理接口的全限定性类名
* 3.映射文件中的statement的id必须和代理接口中的相应方法名称相同
*/
SqlSession sqlSession = null;
IStudentDao studentDao = null;
@Before
public void before() throws IOException {
sqlSession = MyBatisUtil.getSqlSession();
studentDao = sqlSession.getMapper(IStudentDao.class);
System.out.print("代理:"+studentDao);
}
@After
public void after(){
if (sqlSession != null){
sqlSession.close();
}
}
//学生信息插入
@Test
public void test01()
{
Student student = new Student("小明3",20,98.5);
studentDao.insertStudent(student);
sqlSession.commit();
System.out.print("插入后:"+student);
}
}
1.1小知识点
使用动态代理查询时,会根据所需要的结果默认调用方法selectList或selectOne方法。
如果返回结果为List,则默认调用selectList方法,否则都默认调用selectOne方法
如果需要返回map时,需要在对应的方法接口上添加注解@MapKey(value=“对应字段名”)
dao接口中代码
@MapKey(value = "name")
Map<String,Student> selectAllStudentMap();
对应xml中代码
<select id="selectAllStudentMap" resultType="Student">
select id,name,age,score from student
</select>
对应测试类中的代码
//使用动态代理查询时,会根据需要的返回结果默认调用方法selectList或selectOne,
//如果返回结果为List,则默认调用selectList方法,否则都默认调用selectOne方法
//如果需要返回map时,需要在对应的接口方法上添加注解@MapKey(value="")
//查询全部学生信息Map
@Test
public void test08()
{
Map<String,Student> map = studentDao.selectAllStudentMap();
System.out.println(map.get("小明"));
}
1.2小知识点
如果在接口层需要传入2个或者多个参数时,需要使用@Param()注解
1.3小知识点
总结:#{}中间可以填写什么内容
1.传入参数为对象时,中间写对象的属性名
2.传入参数为一个基本数据类型或String类型,中间写任意值,就是一个占位符
3.传入参数为map,map的key对应的值是基本数据类型,中间写map的key
4.传入参数为map,map的key对应的值是对象,中间写map的key.属性
List<Student> selectStudentByCondition3(@Param("name") String name,@Param("age") int age);
注解可以和参数名相同也可以不同,#{}这里填写Param中矫正的参数名
1.4常用查询语句
<?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.yyf.dao.IStudentDao">
<!--
1.parameterType可以省略,如果不省略就必须写正确
2.传入的参数是一个对象,#{
对象的属性名},默认调用对象属性的get方法
-->
<insert id="insertStudent">
insert into student (name,age,score) values (#{
name},#{
age},#{
score})
</insert>
<delete id="deleteStudentById">
delete from student where id = #{
id}
</delete>
<update id="updateStudent">
update student set name=#{
name},age=#{
age},score=#{
score} where id=#{
id}
</update>
<insert id="insertStudentCatchId">
insert into student (name,age,score) values (#{
name},#{
age},#{
score})
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
<!--select @@identity-->
select last_insert_id()
</selectKey>
</insert>
<!--
resultType或者resultMap是必须的,并不是集合的类型,而是集合中单个元素的类型
-->
<select id="selectAllStudent" resultType="Student">
select id,name,age,score from student
</select>
<select id="selectAllStudentMap" resultType="Student">
select id,name,age,score from student
</select>
<select id="selectStudentById" resultType="Student">
select id,name,age,score from student where id = #{
id}
</select>
<select id="selectStudentByName" resultType="Student">
select id,name,age,score from student where name like '%' #{
name} '%'
<!--select id,name,age,score from student where name like concat('%',#{
name},'%')-->
<!--select id,name,age,score from student where name like '%${value}%'-->
</select>
<select id="selectStudentByCondition" resultType="Student">
select id,name,age,score from student
where name like '%' #{
name} '%' and age < #{
age}
</select>
<select id="selectStudentByCondition2" resultType="Student">
<!--select id,name,age,score from student
where age > #{
min} and age < #{
max}-->
select id,name,age,score from student
where age > #{
stu1.age} and age < #{
stu2.age}
</select>
<select id="selectStudentByCondition3" resultType="Student">
select id,name,age,score from student
where name like '%' #{
name} '%' and age < #{
age}
</select>
<!--
总结:#{
}中间可以填写什么内容
1.传入参数为对象时,中间写对象的属性名
2.传入参数为一个基本数据类型或String类型,中间写任意值,就是一个占位符
3.传入参数为map,map的key对应的值是基本数据类型,中间写map的key
4.传入参数为map,map的key对应的值是对象,中间写map的key.属性
-->
</mapper>
1.if的使用
<select id="selectStudentByIf" resultType="Student">
select id,name,age,score from student where 1=1
<if test="name != null and name != ''">
and name like '%' #{
name} '%'
</if>
<if test="age > 0">
and age < #{
age}
</if>
</select>
注意:如果没有写1=1 会报错
2.where的使用
<select id="selectStudentByWhere" resultType="Student">
select id,name,age,score from student
<where>
<if test="name != null and name != ''">
name like '%' #{
name} '%'
</if>
<if test="age > 0">
and age < #{
age}
</if>
</where>
</select>
注意:当where和if搭配时,第一个if标签不要加and,其他的加and
3.choose when otherwise标签
<!--
如果输入了name查询条件,不论age有没有输入,都只按照name查询;
如果没有输入name,只输入了age,按照age条件查询;
如果两个都没有输入,查询不到任何结果。
-->
<select id="selectStudentByChoose" resultType="Student">
select id,name,age,score from student
<where>
<choose>
<when test="name != null and name != ''">
name like '%' #{
name} '%'
</when>
<when test="age > 0">
age < #{
age}
</when>
<otherwise>
1!=1
</otherwise>
</choose>
</where>
</select>
4.1foreach标签 传入array数组
<select id="selectStudentByForeachArray" resultType="Student">
select id,name,age,score from student
<if test="array != null and array.length > 0">
where id in
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{
id}
</foreach>
</if>
</select>
4.2foreach标签 传入list集合
<select id="selectStudentByForeachList" resultType="Student">
select id,name,age,score from student
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{
id}
</foreach>
</if>
</select>
4.3foreach标签 传入list《T》集合
<select id="selectStudentByForeachList2" resultType="Student">
select id,name,age,score from student
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{
stu.id}
</foreach>
</if>
</select>
5.sql标签 include标签 用于替换重复sql语句
<select id="selectStudentByFragment" resultType="Student">
<include refid="xxx"/>
<if test="list != null and list.size > 0">
where id in
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{
stu.id}
</foreach>
</if>
</select>
<sql id="xxx">
select id,name,age,score from student
</sql>
6.if+set标签
修改用户信息时有时候不会修改全部,就使用set标签
<update id="modify" parameterType="User">
update user
<set>
<if test="name!=null">
name=#{
name}
</if>
</set>
where id = #{
id}
</update>
7.if+trim
<update id="modify" parameterType="User">
update user
<trim prefix="set" suffixOverrides="," suffix="where id = #{id}">
<if test="name!=null">
name=#{
name}
</if>
</trim>
</update>
1.一对多(多表连接查询)
1.1实体类的定义(只展示重要部分)
//一方为主查询,关联属性定义在一方
public class Country {
private Integer cid;
private String cname;
//关联属性
private Set<Minister> ministers;
public class Minister {
private Integer mid;
private String mname;
1.2.dao接口
public interface ICountryDao {
Country selectCountryById(int id);
}
1.3.映射文件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.yyf.dao.ICountryDao">
<!--多表链接查询-->
<resultMap id="countryMap" type="Country">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<collection property="ministers" ofType="Minister">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
</collection>
</resultMap>
<select id="selectCountryById" resultMap="countryMap">
select cid,cname,mid,mname from country,minister
where cid=#{
id} and countryId=cid
</select>
</mapper>
注意:属性类型为集合类型时 用collection标签 类型用ofType
<collection property="ministers" ofType="Minister">
属性类型为普通类型时用association 标签 类型用javaType
<association property="ministers" javaType="Minister"> </association>
2.一对多(多表单独查询)
<!--多表单独查询-->
<select id="selectMinisterByCountryId" resultType="Minister">
select mid,mname from minister where countryId=#{
cid}
</select>
<resultMap id="countryMap" type="Country">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<collection property="ministers"
ofType="Minister"
select="selectMinisterByCountryId"
column="cid">
</collection>
</resultMap>
<select id="selectCountryById" resultMap="countryMap">
select cid,cname from country
where cid=#{
id}
</select>
3.多对一(多表连接查询)
3.1实体类
//一方为主查询,关联属性定义在一方
public class Country {
private Integer cid;
private String cname;
//以多方为主查询,关联属性定义在多方
public class Minister {
private Integer mid;
private String mname;
//关联属性,多对一关联属性是一个对象
private Country country;
3.2dao接口
public interface IMinisterDao {
Minister selectMinisterById(int id);
}
3.3映射文件xml
<resultMap id="ministerMap" type="Minister">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
<association property="country" javaType="Country">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</association>
</resultMap>
<select id="selectMinisterById" resultMap="ministerMap">
select mid,mname,cid,cname from minister,country
where mid=#{
id} and cid =countryId
</select>
4.多对一(多表单独查询)
<!--多表单独查询-->
<select id="selectCountryById" resultType="Country">
select cid,cname from country where cid = #{
countryId}
</select>
<resultMap id="ministerMap" type="Minister">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
<association property="country"
javaType="Country"
select="selectCountryById"
column="countryId"/>
</resultMap>
<select id="selectMinisterById" resultMap="ministerMap">
select mid,mname,countryId from minister where mid = #{
id}
</select>
5.一对一(多表连接查询)
5.1实体类
public class Husband {
private Integer hid;
private String hname;
//关联属性
private Wife wife;
public class Wife {
private Integer wid;
private String wname;
5.2dao接口
public interface IHusbandDao {
Husband selectHusbandById(int id);
}
5.3映射文件xml
<!--1:1多表连接查询-->
<resultMap id="husbandMap" type="Husband">
<id column="hid" property="hid"/>
<result column="hname" property="hname"/>
<association property="wife" javaType="Wife">
<id column="wid" property="wid"/>
<result column="wname" property="wname"/>
</association>
</resultMap>
<select id="selectHusbandById" resultMap="husbandMap">
select hid,hname,wid,wname from husband,wife
where hid = #{
id} and wid = wifeId
</select>
6.一对一(多表单独查询)
<!--1:1多表单独查询-->
<select id="selectWifeById" resultType="Wife">
select wid,wname from wife where wid = #{
wifeId}
</select>
<resultMap id="husbandMap" type="Husband">
<id column="hid" property="hid"/>
<result column="hname" property="hname"/>
<association property="wife"
javaType="Wife"
select="selectWifeById"
column="wifeId">
</association>
</resultMap>
<select id="selectHusbandById" resultMap="husbandMap">
select hid,hname,wifeId from husband
where hid = #{
id}
</select>
6.1一对一共享主键连接查询
<!--1:1共享主键多表连接查询-->
<resultMap id="husbandMap" type="Husband">
<id column="hid" property="hid"/>
<result column="hname" property="hname"/>
<association property="wife" javaType="Wife">
<id column="wid" property="wid"/>
<result column="wname" property="wname"/>
</association>
</resultMap>
<select id="selectHusbandById" resultMap="husbandMap">
select hid,hname,wid,wname from husband,wife
where hid = #{
id} and wid = hid
</select>
6.2一对一共享主键单独查询
<!--1:1共享主键多表单独查询-->
<select id="selectWifeById" resultType="Wife">
select wid,wname from wife where wid = #{
wifeId}
</select>
<resultMap id="husbandMap" type="Husband">
<id column="hid" property="hid"/>
<result column="hname" property="hname"/>
<association property="wife"
javaType="Wife"
select="selectWifeById"
column="hid">
</association>
</resultMap>
<select id="selectHusbandById" resultMap="husbandMap">
select hid,hname from husband
where hid = #{
id}
</select>
7.自关联一对多(多表连接)
7.1.1实体类
//作为一方
public class Newslabel {
private Integer id;
private String name;
//关联属性
private Set<Newslabel> children;
7.1.2dao接口
public interface INewslabelDao {
Newslabel selectNewslabelById(int id);
}
7.1.3映射文件xml
<!--自关联1:n的多表连接查询,查询栏目及其子栏目的信息(不能查到其孙栏目信息)-->
<resultMap id="newslabelMap" type="Newslabel">
<id column="ptid" property="id"/>
<result column="ptname" property="name"/>
<collection property="children" ofType="Newslabel">
<id column="cdid" property="id"/>
<result column="cdname" property="name"/>
</collection>
</resultMap>
<select id="selectNewslabelById" resultMap="newslabelMap">
select pt.id ptid,pt.name ptname,cd.id cdid,cd.name cdname
from newslabel pt,newslabel cd
where pt.id=#{
id} and cd.pid=pt.id
</select>
7.1自关联一对多(多表单独)
<!--自关联1:n的多表单独查询,查询栏目及其子孙栏目的信息-->
<select id="selectNewslabelByParent" resultMap="newslabelMap">
select id,name from newslabel where pid = #{
id}
</select>
<resultMap id="newslabelMap" type="Newslabel">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="children"
ofType="Newslabel"
select="selectNewslabelByParent"
column="id"/>
</resultMap>
<select id="selectNewslabelById" resultMap="newslabelMap">
select id,name from newslabel
where id = #{
id}
</select>
7.3自关联一对多(多表单独查询栏目的子孙栏目)
<!--自关联1:n的多表单独查询,查询栏目的子孙栏目的信息-->
<select id="selectNewslabelByParent" resultMap="newslabelMap">
select id,name from newslabel where pid = #{
id}
</select>
<resultMap id="newslabelMap" type="Newslabel">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="children"
ofType="Newslabel"
select="selectNewslabelByParent"
column="id"/>
</resultMap>
7.4自关联一对多(多表单独查询栏目及其父辈栏目)
<!--自关联1:n的多表单独查询,查询栏目及其父辈栏目的信息-->
<resultMap id="newslabelMap" type="Newslabel">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="parent"
javaType="Newslabel"
select="selectNewslabelById"
column="pid"/>
</resultMap>
<select id="selectNewslabelById" resultMap="newslabelMap">
select id,name,pid from newslabel
where id = #{
id}
</select>
8.多对多多表连接查询
8.1实体类
//以student为主查询
public class Student {
private Integer sid;
private String sname;
//关联属性
private Set<Course> courses;
public class Course {
private Integer cid;
private String cname;
8.2dao接口
public interface IStudentDao {
Student selectStudentById(int id);
}
8.3映射文件xml
```java
```java
<!--n:m多表连接查询-->
<resultMap id="studentMap" type="Student">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<collection property="courses" ofType="Course">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</collection>
</resultMap>
<select id="selectStudentById" resultMap="studentMap">
select sid,sname,cid,cname from student,course,middle
where sid = #{
id} and studentId = sid and cid = courseId
</select>
9.多对多的多表单独查询
<!--n:m多单独接查询-->
<select id="selectCourseById" resultType="Course">
select cid,cname from course where cid = #{
courseId}
</select>
<resultMap id="middleMap" type="Middle">
<id column="mid" property="mid"/>
<association property="course"
javaType="Course"
select="selectCourseById"
column="courseId"/>
</resultMap>
<select id="selectMiddleByStudent" resultMap="middleMap">
select mid,studentId,courseId from middle where studentId = #{
sid}
</select>
<resultMap id="studentMap" type="Student">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<collection property="courses"
ofType="Course"
select="selectMiddleByStudent"
column="sid"/>
</resultMap>
<select id="selectStudentById" resultMap="studentMap">
select sid,sname from student
where sid = #{
id}
</select>
10.n:m多表连接+单独查询
<!--n:m多表连接+单独查询-->
<select id="selectCourseById" resultType="Course">
select cid,cname from course where cid = #{
courseId}
</select>
<resultMap id="studentMap" type="Student">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<collection property="courses"
ofType="Course"
select="selectCourseById"
column="courseId"/>
</resultMap>
<select id="selectStudentById" resultMap="studentMap">
select sid,sname,courseId from student,middle
where sid = #{
id} and studentId = sid
</select>
小知识点
1.如果数剧库中的字段名和实体类中的属性名不一致
解决方案1:使用resultMap进行手动映射
<!--字段名和属性名不一致:使用resultMap手动映射-->
<resultMap id="studentMap" type="Student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
</resultMap>
<select id="selectStudentById" resultMap="studentMap">
select sid,sname from student where sid = #{
id}
</select>
解决方案2:给字段起别名,让别名和属性名相同
例:select sid id,sname name from student ( 数剧库中字段名为sid,实体类中为id)
<!--字段名和属性名不一致:给字段起别名,让别名和属性名相同-->
<select id="selectStudentById" resultType="Student">
select sid id,sname name from student where sid = #{
id}
</select>
延迟加载只在多表单独查询时起作用
6.1延迟加载策略默认是关闭的,默认为直接加载。
6.1.1当想使用延迟加载时,可以在mybaits主配置文件中设置settings标签的lazyLoadingEnabled属性设置为ture进行打开,settings标签放置有顺序,需要要在properties标签后放置。
6.1.2打开延迟加载后,默认是深度延迟加载,当用户查询主表详情时,关联表不会被查找,只有当查找到关联表的详情后关联表才被查询。
6.2主配置文件
<?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>
<!--加载jdbc属性文件-->
<properties resource="jdbc.properties"/>
<settings>
<!--打开延迟加载的总开关,默认使用的是深度延迟加载策略-->
<setting name="lazyLoadingEnabled" value="true"/>
//
//
</settings>
<!--给实体类定义别名-->
<typeAliases>
<package name="com.yyf.beans"/>
</typeAliases>
6.3测试类
@Test
public void test()
{
Country country = countryDao.selectCountryById(2);
System.out.println("国家的名称:" + country.getCname());//关联表没有被查找
// System.out.println("国家对应部长的个数:" + country.getMinisters().size());此时关联表才被查询。
}
6.2.1 如果想使用侵入式延迟加载,在settings中设置aggressiveLazyLoading属性为true,同时总开关要开着,不然不起作用
<?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>
<!--加载jdbc属性文件-->
<properties resource="jdbc.properties"/>
<settings>
<!--打开延迟加载的总开关,默认使用的是深度延迟加载策略-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--总开关打开,该配置才起作用,侵入式延迟加载的开关-->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
<!--给实体类定义别名-->
<typeAliases>
<package name="com.yyf.beans"/>
</typeAliases>
6.3.1测试类中当查询到主表的详情时就会查找关联表
@Test
public void test()
{
Country country = countryDao.selectCountryById(2);
System.out.println("国家的名称:" + country.getCname());
}
7.1一级缓存默认开启,无法关闭
一级缓存中存放的结果不是查询结果本身,而是一个map
一级缓存底层实现是一个Map
key:hashCode + statementId + SQL语句
value:查询结果本身
7.1.1两次查询相同的student的只进行了一次对数剧库的查询
7.1.2如果使用不同的方法,即使查找的是相同的id也会对数据库进行两次查找
7.1.3不管增删改是否提交,都会清空一级缓存
//证明:一级缓存是存在的
@Test
public void test01()
{
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:" + student);
//第二次查询
Student student2 = studentDao.selectStudentById(2);
System.out.println("第二次查询:" + student2);
//两次查询相同的student的只进行了一次对数剧库的查询
}
// 证明:一级缓存中存放的结果不是查询结果本身,而是一个map
// 一级缓存底层实现是一个Map
// key:hashCode + statementId + SQL语句
// value:查询结果本身
@Test
public void test02()
{
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:" + student);
//第二次查询
Student student2 = studentDao.selectStudentById2(2);
System.out.println("第二次查询:" + student2);
/如果使用不同的方法,即使查找的是相同的id也会对数据库进行两次查找
}
// 证明:增删改操作刷新一级缓存(清空一级缓存)
@Test
public void test03()
{
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:" + student);
// 插入一个对象
studentDao.insertStudent(new Student());
//第二次查询
Student student2 = studentDao.selectStudentById(2);
System.out.println("第二次查询:" + student2);
//不管增删改是否提交,都会清空一级缓存
}
7.2二级缓存
全局开关 是默认打开的,单个mapper.xml是默认关闭的,如果要使用则需要
1)对应查询的实体类实现 Serializable接口
public class Student implements Serializable {
private Integer id;
private String name;
private int age;
private double score;
public Student() {
}
2)mapper.xml文件中创建标签
<mapper namespace="com.yyf.dao.IStudentDao">
<!--
在该命名空间下打开二级缓存,
size可以设置二级缓存的大小,
eviction可以设置二级缓存的逐出策略,
flushInterval可以设置二级缓存的刷新间隔
-->
<cache size="512" eviction="LRU" flushInterval="10000"/>
<!--
flushCache设置该操作是否刷新二级缓存
-->
<insert id="insertStudent" flushCache="false">
insert into student (name,age,score) values (#{
name},#{
age},#{
score})
</insert>
<!--useCache设置该查询方法是否使用缓存-->
<select id="selectStudentById" resultType="Student" useCache="false">
select id,name,age,score from student where id = #{
id}
</select>
//证明:二级缓存是存在的
@Test
public void test01() throws IOException {
// 第一次查询
SqlSession sqlSession = MyBatisUtil.getSqlSession();
studentDao = sqlSession.getMapper(IStudentDao.class);
Student student = studentDao.selectStudentById2(6);
System.out.println("第一次查询:" + student);
//关闭SqlSession,清空一级缓存
sqlSession.close();
SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
studentDao = sqlSession2.getMapper(IStudentDao.class);
// 第二次查询
Student student2 = studentDao.selectStudentById2(6);
System.out.println("第二次查询:" + student2);
sqlSession2.close();
SqlSession sqlSession3 = MyBatisUtil.getSqlSession();
studentDao = sqlSession3.getMapper(IStudentDao.class);
// 第三次查询
Student student3 = studentDao.selectStudentById2(6);
System.out.println("第三次查询:" + student3);
sqlSession3.close();
}
// 证明:增删改操作对二级缓存的影响
// 增删改操作会刷新(清空)二级缓存
// 1.刷新二级缓存到底执行了那些???
// 二级缓存的底层也是Map
// key:hashCode + statemengId + SQL语句
// value:查询结果
// 刷新二级缓存实际上是将二级缓存中的所有Entry对象的value置为null
// 并没有彻底删除整个Entry对象,即key仍保留
// 2.到底何时可以到DB执行select查询?
// 有两种情况可以到DB执行查询
// 1>Map中根本就不存在要查找的Entry,即key未找到
// 2>Map中存在要查找的key,但value值为null
// 3.增删改默认可以影响二级缓存,但也可以让其不影响二级缓存
// 只需要在增删改的statement中添加属性flushCache="false"即可
7.3使用第三方二级缓存
7.3.1在pow文件中引入第三方缓存所需要的依赖
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
7.3.2mybatis主配置文件
<configuration>
<!--加载jdbc属性文件-->
<properties resource="jdbc.properties"/>
<settings>
<!--使用log4j2的日志-->
<setting name="logImpl" value="LOG4J2"/>
<!--二级缓存的总开关,默认是打开的-->
<setting name="cacheEnabled" value="true"/>
</settings>
7.3.3mapper映射文件
<mapper namespace="com.yyf.dao.IStudentDao">
<!--使用DHCache第三方缓存,把type类型设置为第三方缓存类型-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<property name="timeToLiveSeconds" value="1000"/>
</cache>
<!--
flushCache设置该操作是否刷新二级缓存
-->
<insert id="insertStudent" flushCache="false">
insert into student (name,age,score) values (#{
name},#{
age},#{
score})
</insert>
<!--useCache设置该查询方法是否使用缓存-->
<select id="selectStudentById" resultType="Student" useCache="false">
select id,name,age,score from student where id = #{
id}
</select>
小结:
1.二级缓存可以全局性关闭
当前应用中所有查询均不能使用二级缓存,在mybatis的主配置文件
2.二级缓存可以局部性关闭
可以指定在某一个映射文件中是否使用二级缓存,在映射文件中使用
3.二级缓存可以进行对某个查询方法是否使用二级缓存进行设置
在对应查询标签
package com.yyf.dao;
import com.yyf.beans.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface IStudentDao {
// 1.注解是以@开头,首字母大写的,因为注解是类层级的成员,类层级的成员:类、接口、注解、枚举
// 2.对注解中数组类型的属性赋值,使用{元素1,元素2,.....}
// 3.如果数组类型的属性只有一个元素,那么{}可以省略
// 4.若果注解只使用到了一个属性,并且属性名为value,那么这个value名称可以省略
@Insert(value = {
"insert into student (name,age,score) values(#{name},#{age},#{score})"})
void insertStudent(Student student);
@Delete(value = "delete from student where id = #{xx}")
void deleteStudentById(int id);
@Update("update student set name=#{name},age=#{age},score=#{score} where id = #{id}")
void updateStudent(Student student);
//当我们向主键是自增型的数据库表中(如 user 表)插入一条记录时,我们期望获取刚刚写入这条记录
//的id,(因为某些场景下需要获得这个id去做其他的操作),此时我们需要使用SELECT LAST_INSERT_ID()
//这个语句来获取插入记录后返回的自增id的值;
@Insert("insert into student (name,age,score) values(#{name},#{age},#{score})")
@SelectKey(statement = "select last_insert_id()",keyProperty = "id",before = false,resultType = int.class)
void insertStudentCatchId(Student student);
@Select("select id,name,age,score from student")
List<Student> selectAllStudent();
@MapKey(value = "name")
@Select("select id,name,age,score from student")
Map<String,Student> selectAllStudentMap();
@Select("select id,name,age,score from student where id = #{xx}")
Student selectStudentById(int id);
@Select("select id,name,age,score from student where name like '%' #{name} '%'")
List<Student> selectStudentByName(String name);
@Select("select id,name,age,score from student where name like '%' #{name} '%' and age < #{age}")
List<Student> selectStudentByCondition(Student student);
//@Select("select id,name,age,score from student where age < #{max} and age > #{min}")
@Select("select id,name,age,score from student where age < #{stu2.age} and age > #{stu1.age}")
List<Student> selectStudentByCondition2(Map map);
@Select("select id,name,age,score from student where name like '%' #{name} '%' and age < #{age}")
List<Student> selectStudentByCondition3(@Param("name") String name,@Param("age") int age);
}
九,mybatis注解动态sql
9.1dao接口
public interface IStudentDao {
@InsertProvider(type = MySqlProvider.class, method = "getInsertSql")
void insertStudent(Student student);
@DeleteProvider(type = MySqlProvider.class, method = "getDeleteSql")
void deleteStudentById(int id);
//@UpdateProvider(type = MySqlProvider.class, method = "getUpdateSql")
//@UpdateProvider(type = MySqlProvider.class, method = "getUpdateSql2")
@UpdateProvider(type = MySqlProvider.class, method = "getUpdateSql3")
void updateStudent(Student student);
@InsertProvider(type = MySqlProvider.class, method = "getInsertSql")
@SelectKey(statement = "select last_insert_id()",keyProperty = "id",before = false,resultType = int.class)
void insertStudentCatchId(Student student);
@SelectProvider(type = MySqlProvider.class, method = "getSelectSql")
List<Student> selectAllStudent();
@SelectProvider(type = MySqlProvider.class, method = "getSelectSql")
Student selectStudentById(Integer id);
@SelectProvider(type = MySqlProvider.class, method = "getSelectSql2")
List<Student> selectStudentByName(String name);
@MapKey(value = "name")
@SelectProvider(type = MySqlProvider.class,method = "getSelectSql")
Map<String,Student> selectAllStudentMap();
@SelectProvider(type = MySqlProvider.class, method = "getSelectSql3")
List<Student> selectStudentByCondition(Student student);
@SelectProvider(type = MySqlProvider.class, method = "getSelectSql4")
List<Student> selectStudentByCondition2(Map map);
//@SelectProvider(type = MySqlProvider.class, method = "getSelectSql5")
@SelectProvider(type = MySqlProvider.class, method = "getSelectSql6")
List<Student> selectStudentByCondition3(@Param("name") String name,@Param("age") int age);
}
9.2MySqlProvider类
package com.yyf.dao;
import com.yyf.beans.Student;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.jdbc.SQL;
import java.util.Map;
/**
* company: www.abc.com
* Author: Administrator
* Create Data: 2020/10/26 0026
*/
public class MySqlProvider {
public String getInsertSql(){
return "insert into student (name,age,score) values(#{name},#{age},#{score})";
}
public String getDeleteSql(){
return "delete from student where id = #{id}";
}
public String getUpdateSql(){
return "update student set name=#{name},age=#{age},score=#{score} where id = #{id}";
}
public String getSelectSql(Integer id){
if (id != null){
return "select id,name,age,score from student where id = #{id}";
}else {
return "select id,name,age,score from student";
}
}
public String getSelectSql2(){
return "select id,name,age,score from student where name like '%' #{name} '%'";
}
public String getSelectSql3(Student student){
StringBuffer sql = new StringBuffer();
sql.append("select id,name,age,score from student where 1=1");
if (student.getName() != null && !"".equals(student.getName())){
sql.append(" and name like '%' #{name} '%'");
}
if (student.getAge() > 0){
sql.append(" and age < #{age}");
}
return sql.toString();
}
public String getSelectSql4(Map map){
StringBuffer sql = new StringBuffer();
sql.append("select id,name,age,score from student where 1=1");
if ((Integer)map.get("min") > 0){
sql.append(" and age > #{min}");
}
if ((Integer)map.get("max") > 0){
sql.append(" and age < #{max}");
}
return sql.toString();
}
public String getSelectSql5(@Param("name") String name, @Param("age") int age){
StringBuffer sql = new StringBuffer();
sql.append("select id,name,age,score from student where 1=1");
if (name != null && !"".equals(name)){
sql.append(" and name like '%' #{name} '%'");
}
if (age > 0){
sql.append(" and age < #{age}");
}
return sql.toString();
}
public String getSelectSql6(Map map){
StringBuffer sql = new StringBuffer();
sql.append("select id,name,age,score from student where 1=1");
if (map.get("name") != null && !"".equals(map.get("name"))){
sql.append(" and name like '%' #{name} '%'");
}
if ((Integer)map.get("age") > 0){
sql.append(" and age < #{age}");
}
return sql.toString();
}
public String getUpdateSql2(Student student){
StringBuffer sql = new StringBuffer();
//update student set name=#{name},age=#{age},score=#{score} where id = #{id}
sql.append("update student set id = #{id}");
if (student.getName() != null && !"".equals(student.getName())){
sql.append(",name = #{name}");
}
if (student.getAge() > 0){
sql.append(",age = #{age}");
}
if (student.getScore() > 0){
sql.append(",score = #{score}");
}
sql.append(" where id = #{id}");
return sql.toString();
}
public String getUpdateSql3(final Student student){
// new SQL(){}表示创建一个SQL类的子类的对象,只不过这个子类叫什么名称不知道,也没有指定
// 所以,这种写法叫做匿名内部类
return new SQL(){
// 下面的代码在何时被执行?
// 当当前类(SQL类的子类)的无参构造器被调用时,会自动执行这段代码
// 在类中的{}称为实例代码块
{
this.UPDATE("student");
this.SET("id = #{id}");
if (student.getName() != null && !"".equals(student.getName())){
this.SET("name = #{name}");
}
if (student.getAge() > 0){
this.SET("age = #{age}");
}
if (student.getScore() > 0){
this.SET("score = #{score}");
}
this.WHERE("id = #{id}");
}
}.toString();
}
}
9.3测试类
public class MyTest
{
SqlSession sqlSession = null;
IStudentDao studentDao = null;
@Before
public void before() throws IOException {
sqlSession = MyBatisUtil.getSqlSession();
studentDao = sqlSession.getMapper(IStudentDao.class);
//System.out.print("代理:"+studentDao);
}
@After
public void after(){
if (sqlSession != null){
sqlSession.close();
}
}
//学生信息插入
@Test
public void test01()
{
Student student = new Student("小明3",20,98.5);
studentDao.insertStudent(student);
sqlSession.commit();
System.out.print("插入后:"+student);
}
//删除学生信息按id
@Test
public void test02()
{
studentDao.deleteStudentById(18);
sqlSession.commit();
}
//修改学生信息按id
@Test
public void test03()
{
Student student = new Student("小绿",0,0);
student.setId(10);
studentDao.updateStudent(student);
sqlSession.commit();
}
//获取最新插入的学生id
@Test
public void test04()
{
Student student = new Student("小小1",18,88);
studentDao.insertStudentCatchId(student);
sqlSession.commit();
System.out.print("插入后:"+student);
}
//查询全部学生信息List
@Test
public void test05()
{
List<Student> students = studentDao.selectAllStudent();
for (Student s:students){
System.out.println(s);
}
}
//按照id查询学生信息
@Test
public void test06()
{
Student student = studentDao.selectStudentById(6);
System.out.println(student);
}
//按照name查询学生信息
@Test
public void test07()
{
List<Student> students = studentDao.selectStudentByName("明");
for (Student s:students){
System.out.println(s);
}
}
//使用动态代理查询时,会根据需要的返回结果默认调用方法selectList或selectOne,
//如果返回结果为List,则默认调用selectList方法,否则都默认调用selectOne方法
//如果需要返回map时,需要在对应的接口方法上添加注解@MapKey(value="")
//查询全部学生信息Map
@Test
public void test08()
{
Map<String,Student> map = studentDao.selectAllStudentMap();
System.out.println(map.get("小明3"));
}
//复合条件查询学生信息
@Test
public void test09()
{
//Student student = new Student("明",20,-1);
//Student student = new Student("明",0,-1);
//Student student = new Student(null,20,-1);
Student student = new Student(null,0,-1);
List<Student> students = studentDao.selectStudentByCondition(student);
for (Student s:students){
System.out.println(s);
}
}
//复合条件查询学生信息2
@Test
public void test10()
{
Map map = new HashMap();
/*Student stu1 = new Student(null,16,-1);
Student stu2 = new Student(null,20,-1);
map.put("stu1",stu1);
map.put("stu2",stu2);*/
map.put("min",16);
map.put("max",20);
List<Student> students = studentDao.selectStudentByCondition2(map);
for (Student s:students){
System.out.println(s);
}
}
//复合条件查询学生信息3
@Test
public void test11()
{
List<Student> students = studentDao.selectStudentByCondition3("明",20);
for (Student s:students){
System.out.println(s);
}
}
}
完结…