MyBatis框架学习笔记(四):动态SQL语句、映射关系和缓存
1 动态 SQL 语句-更复杂的查询业务需求
1.1 动态 SQL-官方文档
(1)文档地址: mybatis – MyBatis 3 | 动态 SQL
(2)为什么需要动态 SQL
- 动态 SQL 是 MyBatis 的强大特性之一
- 使用 JDBC 或其它类似的框架,根据不同条件拼接 SQL 语句非常麻烦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号等
- SQL 映射语句中的强大的动态 SQL 语言, 可以很好的解决这个问题
1.2 动态 SQL-基本介绍
- 在一个实际的项目中,sql 语句往往是比较复杂的
- 为了满足更加复杂的业务需求,MyBatis 的设计者,提供了动态生成 SQL 的功能。
动态 SQL 必要性
- 比如我们查询Monster 时,如果程序员输入的age 不大于0, 我们的sql语句就不带age 。
- 更新 Monster 对象时,没有设置的新的属性值,就保持原来的值,设置了新的值,才更新
解决方案分析
- 从上面的需求我们可以看出,有时我们在生成 sql 语句时,在同一个方法中,还要根据 不同的情况生成不同的 sql 语句
- 解决方案: MyBatis 提供的动态 SQL 机制
动态 SQL 常用标签
动态 SQL 提供了如下几种常用的标签,类似我们 Java 的控制语句:
(1)if [判断]
(2)where [拼接 where 子句]
(3)choose/when/otherwise [类似 java 的 switch 语句, 注意是单分支]
(4)foreach [类似 in ]
(5)trim [替换关键字/定制元素的功能]
(6)set [在 update 的 set 中,可以保证进入 set 标签的属性被修改,而没有进入 set 的,保持原来的值]
1.3 动态 SQL-案例演示
1.3.1 新建 Module dynamic-sql
(1)在原来的项目中,新建 dynamic-sql 模块演示动态 SQL 的使用
(2)新建 Module 后,先创建需要的包,再将需要的文件/资源拷贝过来(这里我们拷贝 Monster.java、resources/jdbc.properties 和 mybatis-config.xml)
(3)创建 MonsterMapper.java MonsterMapper.xml 和 MonsterMapperTest.java
1.3.2 if 标签应用实例
需求:请查询 age 大于 10 的所有妖怪,如果程序员输入的 age 不大于 0, 则输出所有 的妖怪
代码实现
(1)修改 MonsterMapper.java ,增加接口方法
//根据age查询结果
public List<Monster> findMonsterByAge(@Param(value = "age") Integer age);
(2)修改 MonsterMapper.xml,sql实现
<!--
1. 配置方法public List<Monster> findMonsterByAge(@Param(value="age") Integer age);
2. 请查询age 大于 10 的所有妖怪,如果程序员输入的age 不大于 0, 则输出所有的妖怪
3. 如果使用原来的#{age} 在test表达式是取不出入参值
4. 解决方案是使用@Param
-->
<select id="findMonsterByAge" parameterType="int" resultType="monster">select * from `monster` where 1 = 1<if test="age >= 0">and `age` > ${age}</if>
</select>
(3)修改 MonsterMapperTest.java 增加测试方法
@Test
public void findMonsterByAge() {List<Monster> monsters =monsterMapper.findMonsterByAge(-1);for (Monster monster : monsters) {System.out.println("monster--" + monster);}if (sqlSession != null) {sqlSession.close();}System.out.println("操作成功~");
}
测试效果
1.3.3 where 标签应用实例
需求:查询 id 大于 20 的,并且名字包含 "狐狸精" 的所有妖怪, 如果名字为空 , 就不带名字条件, 如果输入的 id 小于 0, 就不带 id 的条件
(1)修改 MonsterMapper.java ,增加接口方法
//根据id和名字来查询结果
public List<Monster> findMonsterByIdAndName(Monster monster);
(2)修改 MonsterMapper.xml,sql实现
<!--1、配置public List<Monster> findMonsterByIdAndName(Monster monster);2. 如果我们入参是对象,test表达式中, 直接使用对象的属性名即可3. where标签,会在组织动态sql时,加上where4. mybatis底层自动的去掉多余的AND
-->
<select id="findMonsterByIdAndName" parameterType="monster" resultType="monster">select * from `monster`<where><if test="age >= 0">`age` > #{age}</if><if test="name != null and name != ''">and `name` like '%${name}%'</if></where>
</select>
(3)修改 MonsterMapperTest.java 增加测试方法
@Test
public void findMonsterByIdAndName(){Monster monster = new Monster();monster.setAge(20);monster.setName("狐狸精");List<Monster> monsters =monsterMapper.findMonsterByIdAndName(monster);for (Monster o : monsters) {System.out.println("monster--" + o);}if (sqlSession != null){sqlSession.close();}System.out.println("操作成功~");
}
测试效果
1.3.4 choose/when/otherwise 应用实例
需求:如果给的 name 不为空,就按名字查询妖怪,name 为空并且 id>0,就按 id 来查询妖怪, 如果name 为空 并且 id<0,则以 id > 10 为查询条件。要求使用 choose/when/otherwise 标签实现, 传入参数要求使用 Map
(1)修改 MonsterMapper.java ,增加接口方法
//测试choose标签的使用
public List<Monster> findMonsterByIdOrName_choose(Map<String, Object> map);
(2)修改 MonsterMapper.xml,sql实现
<!--1、配置/实现public List<Monster> findMonsterByIdOrName_choose(Map<String, Object> map)2、 1) 如果给的name不为空,就按名字查询妖怪,2) 如果指定的id>0,就按id来查询妖怪3) 如果前面两个条件都不满足, 就默认查询 `age` > 10的4) 使用mybatis 提供choose-when-otherwise-->
<select id="findMonsterByIdOrName_choose" parameterType="map" resultType="monster">select * from `monster`<where><choose><when test="name != null and name != ''">`name` like '%${name}%'</when><when test="age >= 0">`age` > #{age}</when><otherwise>`age` > 10</otherwise></choose></where>
</select>
(3)修改 MonsterMapperTest.java 增加测试方法
@Test
public void findMonsterByIdOrName_choose(){HashMap<String, Object> map = new HashMap<>();map.put("age",-1);map.put("name","");List<Monster> monsters =monsterMapper.findMonsterByIdOrName_choose(map);for (Monster o : monsters) {System.out.println("monster--" + o);}if (sqlSession != null){sqlSession.close();}System.out.println("操作成功~");
}
测试效果
1.3.5 forEach 标签应用实例
forEach 标签说明:可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
需求:查询 monster_id 为 20, 22, 34 的妖怪
(1)修改 MonsterMapper.java ,增加接口方法
//测试foreach的标签使用
public List<Monster> findMonsterById_forEach(Map<String, Object> map)
(2)修改 MonsterMapper.xml,sql实现
<!--1、配置/实现public List<Monster> findMonsterById_forEach(Map<String, Object> map);2、查询id 为 5, 6, 7 的妖怪3、是foreach标签4. 入参map 中 会如何传入id值 k-v ids - [集合,比如List 10,12,14], 即map 入参中应当有 ids-[10,12,14]-->
<select id="findMonsterById_forEach" parameterType="map" resultType="monster">select * from `monster`<!-- 解读3. 如果ids不为空,则使用foreach标签进行遍历4. collection="ids" 对应入参map的 key 为 ids5. item="id" 在遍历ids集合时,每次取出的值,对应的变量id6. open="where id in (" 表示开头的字符串7. separator="," 遍历出来的多个值之间的 分隔符号8. close=")" 表示结尾的字符串9. #{id} 对应的就是 item="id",即遍历出来的值--><if test="ids != null and ids != ''"><foreach collection="ids" index="" item="id" open="where id in ("separator="," close=")">#{id}</foreach></if>
</select>
(3)修改 MonsterMapperTest.java 增加测试方法
@Test
public void findMonsterById_forEach() {Map<String, Object> map = new HashMap<>();map.put("ids", Arrays.asList(5, 6, 7));List<Monster> monsters =monsterMapper.findMonsterById_forEach(map);for (Monster monster : monsters) {System.out.println("monster--" + monster);}if (sqlSession != null) {sqlSession.close();}System.out.println("操作成功~");}
测试效果
1.3.6 trim 标签应用实例
trim 可以替换一些关键字
要求:按名字查询妖怪,在动态添加where,并且如果where 子句以开头 and | or 就去除。即使用trim 标签 实现 等价于 where标签的效果
(1)修改 MonsterMapper.java ,增加接口方法
//trim标签的使用
public List<Monster> findMonsterByName_Trim(Map<String, Object> map);
(2)修改 MonsterMapper.xml,sql实现
suffixOverrides 表示行末尾,prefixOverrides 表示行开头
<!--
1. 配置/实现 public List<Monster> findMonsterByName_Trim(Map<String, Object> map);
2. 按名字和年龄 查询妖怪,如果sql语句开头有 and | or 就替换成 where
3. 如果要实现这个功能,其实使用where标签是一样的效果 [加入where 同时会去掉多余的and]
4. trim prefix="WHERE" prefixOverrides="and|or|wwj" 若子句的开头为 “AND” 或 “OR 或"wwj"
, 就去除
-->
<select id="findMonsterByName_Trim" parameterType="map" resultType="Monster">SELECT * FROM `monster`<trim prefix="WHERE" prefixOverrides="and|or|wwj"><if test="name != null and name != ''">AND `name` = #{name}</if><if test="age != null and age != ''">AND `age` > #{age}</if></trim>
</select>
(3)修改 MonsterMapperTest.java 增加测试方法
@Test
public void findMonsterByName_Trim() {Map<String, Object> map = new HashMap<>();map.put("name", "狐狸精-100");map.put("age", 10);List<Monster> monsters =monsterMapper.findMonsterByName_Trim(map);for (Monster monster : monsters) {System.out.println("monster--" + monster);}if (sqlSession != null) {sqlSession.close();}System.out.println("操作成功~");}
测试效果
1.3.7 set 标签应用实例
set标签说明:
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
需求: 请对指定 id 的妖怪进行 修改,如果没有设置新的属性,则保持原来的值
(1)修改 MonsterMapper.java ,增加接口方法
//测试Set标签
public void updateMonster_set(Map<String, Object> map);
(2)修改 MonsterMapper.xml,sql实现
<!--
1. 配置/实现 public void updateMonster_set(Map<String, Object> map);
2. 请对指定id 的妖怪进行 修改,如果没有设置新的属性,则保持原来的值
3. 入参要根据sql语句来配合 map [age-10, email-'xx@sohu.com'...]
4. set标签会处理多余的 ,
-->
<update id="updateMonster_set" parameterType="map">UPDATE `monster`<set><if test="age != null and age != ''">`age` = #{age} ,</if><if test="email != null and email != ''">`email` = #{email} ,</if><if test="name != null and name != ''">`name` = #{name} ,</if><if test="birthday != null and birthday != ''">`birthday` = #{birthday} ,</if><if test="salary != null and salary != ''">`salary` = #{salary} ,</if><if test="gender != null and gender != ''">`gender` = #{gender} ,</if></set>WHERE id = #{id}
</update>
(3)修改 MonsterMapperTest.java 增加测试方法
@Test
public void updateMonster_set() {Map<String, Object> map = new HashMap<>();map.put("id", 1);map.put("name", "牛魔王5-100");map.put("age", 76);map.put("email", "wwj@qq.com");monsterMapper.updateMonster_set(map);//修改需要有commitif (sqlSession != null) {sqlSession.commit();sqlSession.close();}System.out.println("操作成功~");
}
测试效果
2 映射关系一对一
2.1 映射关系-官方文档
文档地址: mybatis – MyBatis 3 | XML 映射器
2.2 映射关系 1 对 1-基本介绍
基本介绍:项目中 1 对 1 的关系是一个基本的映射关系,比如:Person(人) --- IDCard(身份证)
2.3 映射关系 1 对 1-映射方式
2.3.1 映射方式
(1)通过配置 XxxMapper.xml 实现 1 对 1 [配置方式]
(2)通过注解的方式实现 1 对 1 [注解方式]
2.3.2 配置 Mapper.xml 的方式-应用实例
2.3.2.1 方式1
说明:通过配置 XxxMapper.xml 的方式来实现下面的 1 对 1 的映射关系,实现级联查询,通过 person 可以获取到对应的 idencard 信息
完成功能示意(如下)
person--Person{id=1, name='张三', card=IdenCard{id=1,card_sn='111111111111110'}}
代码实现
(1)创建 person 表和 idencard 表
CREATE TABLE idencard(
id INT PRIMARY KEY AUTO_INCREMENT,
card_sn VARCHAR(32) NOT NULL DEFAULT ''
);CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL DEFAULT '',
card_id INT , --对应idencard的主键id
FOREIGN KEY (card_id) REFERENCES idencard(id) --外键
);INSERT INTO idencard VALUES(1,'111111111111110');
INSERT INTO person VALUES(1,'张三',1);
SELECT * FROM person;
SELECT * FROM idencard;
(2)创建新的 module(mybatis-mapping), 相关配置文件可以从上一个 module 拷贝 (这里我们拷贝resources/jdbc.properties 和 mybatis-config.xml)。嫌麻烦这一步可以省略,这里这是为了直观,所以一个章节使用一个module
(3)创 建 entity: src\main\java\com\entity\IdenCard.java 和 src\main\java\com\entity\Person.java
package com.entity;public class IdenCard {private Integer id;private String card_sn;//通过查询IdenCard 可以级联查询得到personprivate Person person;public Person getPerson() {return person;}public void setPerson(Person person) {this.person = person;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getCard_sn() {return card_sn;}public void setCard_sn(String card_sn) {this.card_sn = card_sn;}@Overridepublic String toString() {return "IdenCard{" +"id=" + id +", card_sn='" + card_sn + '\'' +", person=" + person +'}';}
}
package com.entity;public class Person {private Integer id;private String name;//因为我们的需要实现一个级联操作, 一个人需要对应一个身份证//这里需要直接定义IdenCard对象属性private IdenCard card;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 IdenCard getCard() {return card;}public void setCard(IdenCard card) {this.card = card;}@Overridepublic String toString() {return "Person{" +"id=" + id +", name='" + name + '\'' +", card=" + card +'}';}
}
(4)创建 com\mapper\IdenCardMapper.java接口 和 com\mapper\IdenCardMapper.xml映射文件
package com.hspedu.mapper;import com.hspedu.entity.IdenCard;public interface IdenCardMapper {//根据id获取到身份证序列号public IdenCard getIdenCardById(Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mapper.IdenCardMapper"><select id="getIdenCardById" parameterType="integer" resultType="idenCard">select * from `idencard` where `id`=#{id}</select>
</mapper>
(5)创建 PersonMapper.java 和 PersonMapper.xml
package com.mapper;import com.entity.Person;public interface PersonMapper {//通过Person的id获取到Person,包括这个Person关联的IdenCard对象[级联查询]public Person getPersonById(Integer id);}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mapper.PersonMapper"><!--1、配置/实现public Person getPersonById(Integer id);2、完成通过Person的id获取到Person,包括这个Person关联的IdenCard对象[级联查询]3. 自定义resultMap 搞定 映射返回的结果--><resultMap id="PersonResultMap" type="Person"><!--id标签 – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能1.property="id" 表示person 属性 id ,通常是主键2.column="id" 表示对应表的字段--><id property="id" column="id"/><result property="name" column="name"/><!--association标签 – 一个复杂类型的关联1. property="card" 表示 Person对象的 card 属性2. javaType="IdenCard" 表示card 属性 的类型3. column="id" 是从我们的 下面这个语句查询后返回的字段SELECT * FROM `person`,`idencard` WHERE `person`.id=1AND `person`.card_id = `idencard`.id--><association property="card" javaType="IdenCard"><result property="id" column="id"/><result property="card_sn" column="card_sn"/></association></resultMap><select id="getPersonById" parameterType="Integer"resultMap="PersonResultMap">SELECT * FROM `person`,`idencard` WHERE `person`.id = #{id}AND `person`.card_id = `idencard`.id</select></mapper>
(6)创建 src\test\java\com\mapper\IdenCardMapperTest.java 和src\test\java\com\map per\PersonMapperTest.java , 进行测试
package com.mapper;import com.entity.IdenCard;
import com.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;public class IdenCardMapperTest {SqlSession sqlSession;IdenCardMapper idenCardMapper;@Beforepublic void init(){sqlSession = MyBatisUtils.getSqlSession();idenCardMapper = sqlSession.getMapper(IdenCardMapper.class);}@Testpublic void getIdenCardById(){int id = 1;IdenCard idenCard = idenCardMapper.getIdenCardById(id);System.out.println("idenCard=" + idenCard);}
}
package com.mapper;import com.entity.Person;
import com.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;public class PersonMapperTest {SqlSession sqlSession;PersonMapper personMapper;@Beforepublic void init(){sqlSession = MyBatisUtils.getSqlSession();personMapper = sqlSession.getMapper(PersonMapper.class);}@Testpublic void getPersonById(){Person person = personMapper.getPersonById(1);System.out.println("person--" + person);if (sqlSession != null){sqlSession.close();}}}
测试效果
2.3.2.2 方式2
(1)修改PersonMapper.java 接口增加方法 和 PersonMapper.xml 配置相应sql映射
//通过Person的id获取到Person,包括这个Person关联的IdenCard对象,方式2
public Person getPersonById2(Integer id);
<!--1、方式21) 先通过 SELECT * FROM `person` WHERE `id` = #{id} 返回 person信息2) 再通过 返回的card_id 值,再执行操作,得到IdenCard 数据
-->
<resultMap id="PersonResultMap2" type="Person"><id property="id" column="id"/><result property="name" column="name"/><!--1. mybatis第二种方式核心思想: 将多表联查,分解成单表操作 , 这样简洁,而且易于维护 ,推荐2. property="card": 表示 Person对象的 card 属性3. column="card_id" 这个是SELECT * FROM `person` WHERE `id` = #{id} 返回的 字段 card_id 信息/数据4. 返回的 字段 card_id 信息/数据 作为getIdenCardById入参, 来执行--><association property="card" column="card_id"select="com.hspedu.mapper.IdenCardMapper.getIdenCardById" />
</resultMap>
<select id="getPersonById2" parameterType="Integer" resultMap="PersonResultMap2">SELECT * FROM `person` WHERE `id` = #{id}
</select>
(2)修改 PersonMapperTest.java 进行测试
@Test
public void getPersonById2(){Person person = personMapper.getPersonById2(1);System.out.println("person--" + person);if (sqlSession != null){sqlSession.close();}
}
测试效果
2.3.3 注解的方式实现-应用实例
说明:通过注解的方式来实现下面的 1 对 1 的映射关系,实现级联查询,通过 person 可以获取到 对应的 identcard 信息 (在实际开发中还是推荐使用配置方式)
(1)创建 com\mapper\IdenCardMapperAnnotaion.java
package com.mapper;import com.entity.IdenCard;
import org.apache.ibatis.annotations.Select;/*** IdenCardMapperAnnotation: 使用注解方式实现1对1的映射*/
public interface IdenCardMapperAnnotation {//根据id获取到身份证//这个方法不需要返回任何级联对象@Select("SELECT * FROM `idencard` WHERE `id` = #{id}")public IdenCard getIdenCardById(Integer id);
}
(2)创建 com\mapper\PersonMapperAnnotation.java
package com.mapper;import com.entity.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;public interface PersonMapperAnnotation {//这里注解实现方法//注解的形式就是对前面xml配置方式的体现@Select("SELECT * FROM `person` WHERE `id` = #{id}")@Results({@Result(id = true, property = "id", column = "id"),@Result(property = "name", column = "name"),@Result(property = "card", column = "card_id",one = @One(select = "com.mapper.IdenCardMapper.getIdenCardById"))})public Person getPersonById(Integer id);
}
(3)创建 com\mapper\PersonMapperAnnotationTest.java 完成测试
package com.mapper;import com.entity.Person;
import com.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;public class PersonMapperAnnotationTest {//属性private SqlSession sqlSession;private PersonMapperAnnotation personMapperAnnotation;//初始化@Beforepublic void init() {//获取到sqlSessionsqlSession = MyBatisUtils.getSqlSession();personMapperAnnotation = sqlSession.getMapper(PersonMapperAnnotation.class);}@Testpublic void getPersonById() {Person person = personMapperAnnotation.getPersonById(1);System.out.println("person----" + person);if(sqlSession != null) {sqlSession.close();}}
}
测试效果
3 映射关系多对一
3 .1 映射关系-官方文档
文档地址: mybatis – MyBatis 3 | XML 映射器
3.2 映射关系多对 1-基本介绍
基本介绍
(1)项目中多对 1 的关系是一个基本的映射关系, 多对 1, 也可以理解成是 1 对多
- User --- Pet: 一个用户可以养多只宠物
- Dep ---Emp : 一个部门可以有多个员工
特别说明
(1)在实际的项目开发中, 要求会使用双向的多对一的映射关系
(2)说明:什么是双向的多对一的关系 : 比如通过 User 可以查询到对应的 Pet, 反过来,通过 Pet 也可以级联查询到对应的 User 信息
(3)多对多的关系,是在多对 1 的基础上扩展即可.
3.3 映射关系多对 1-映射方式
3.3.1 映射方式
(1)方式 1:通过配置 XxxMapper.xml 实现多对 1
(2)方式 2:通过注解的方式实现 多对 1
3.3.2 配置 Mapper.xml 方式-应用实例
需求说明: 实现级联查询,通过 user 的 id 可以查询到用户信息,并可以查询到关联的 pet 信息,反过来,通过 Pet 的 id 可以查询到 Pet 的信息,并且可以级联查询到它的主人 User 对象信息
(1)创建 mybatis_user 和 mybatis_pet 表
CREATE TABLE mybatis_user(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL DEFAULT ''
);
CREATE TABLE mybatis_pet(
id INT PRIMARY KEY AUTO_INCREMENT,
nickname VARCHAR(32) NOT NULL DEFAULT '',
user_id INT ,
FOREIGN KEY (user_id) REFERENCES mybatis_user(id)
);
INSERT INTO mybatis_user
VALUES(NULL,'宋江'),(NULL,'张飞');
INSERT INTO mybatis_pet
VALUES(1,'黑背',1),(2,'小哈',1);
INSERT INTO mybatis_pet
VALUES(3,'波斯猫',2),(4,'贵妃猫',2);
SELECT * FROM mybatis_user;
SELECT * FROM mybatis_pet;
(2)创 建 entity : entity\Pet.java 和 entity\User.java
package com.entity;import java.util.List;public class User {private Integer id;private String name;//因为一个user可以养多个宠物,mybatis 使用集合List<Pet>体现这个关系private List<Pet> pets;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 List<Pet> getPets() {return pets;}public void setPets(List<Pet> pets) {this.pets = pets;}//这toString会带来麻烦?=>会造成StackOverFlow//@Override//public String toString() {// return "User{" +// "id=" + id +// ", name='" + name + '\'' +// ", pets=" + pets +// '}';//}
}
package com.entity;public class Pet {private Integer id;private String nickname;//一个pet对应一个主人 User对象private User user;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}}
(3)创建 PetMapper.java 和 UserMapper.java
package com.mapper;import com.entity.Pet;import java.util.List;public interface PetMapper {//通过User的id来获取pet对象,可能有多个,因此使用List接收public List<Pet> getPetByUserId(Integer userId);//通过pet的id获取Pet对象, 同时会查询到pet对象关联的user对象public Pet getPetById(Integer id);}
package com.mapper;import com.entity.User;public interface UserMapper {//通过id获取User对象public User getUserById(Integer id);
}
(4)创建 UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mapper.UserMapper"><!--1、配置/实现 public User getUserById(Integer id);2、思路(1) 先通过user-id 查询得到user信息 (2) 再根据user-id查询对应的pet信息并映射到User-List<Pet> pets--><resultMap id="userMap" type="com.entity.User"><id property="id" column="id"/><result property="name" column="name"/><!--因为pets属性是集合,因此这里需要是collection标签来处理1. ofType="Pet" 指定返回的集合中存放的数据类型Pet2. collection 表示 pets 是一个集合3. property="pets" 是返回的user对象的属性 pets4. column="id" SELECT * FROM `mybatis_user` WHERE `id` = #{id} 返回的id字段对应的值--><collection property="pets" column="id" ofType="pet"select="com.mapper.PetMapper.getPetByUserId"/></resultMap><select id="getUserById" parameterType="integer" resultMap="userMap">select * from `mybatis_user` where id=#{id}</select></mapper>
(5) 创建 PetMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.mapper.PetMapper"><!--1、通过User的id来获取pet对象,可能有多个,因此使用List接收2、public List<Pet> getPetByUserId(Integer userId);--><resultMap id="PetResultMap" type="Pet"><id property="id" column="id"/><result property="nickname" column="nickname"/><association property="user" column="user_id"select="com.mapper.UserMapper.getUserById" /></resultMap><select id="getPetByUserId" parameterType="integer" resultMap="PetResultMap">select * from `mybatis_pet` where user_id=#{userId}</select><!--1. 这里直接复用PetResultMap2. 实现/配置public Pet getPetById(Integer id);3. 通过pet的id获取Pet对象--><select id="getPetById"parameterType="Integer"resultMap="PetResultMap">SELECT * FROM `mybatis_pet` WHERE `id` = #{id}</select>
</mapper>
(6)创建 PetMapperTest.java 完成测试
package com.mapper;import com.entity.Pet;
import com.entity.User;
import com.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;import java.util.List;public class PetMapperTest {//属性private SqlSession sqlSession;private PetMapper petMapper;//初始化@Beforepublic void init() {//获取到sqlSessionsqlSession = MyBatisUtils.getSqlSession();petMapper = sqlSession.getMapper(PetMapper.class);}@Testpublic void getPetByUserId() {List<Pet> pets = petMapper.getPetByUserId(2);for (Pet pet : pets) {System.out.println("pet信息-" + pet.getId() + "-" + pet.getNickname());User user = pet.getUser();System.out.println("user信息 name-" + user.getName());}if(sqlSession != null) {sqlSession.close();}}@Testpublic void getPetById() {Pet pet = petMapper.getPetById(2);System.out.println("pet信息-" + pet.getId() + "-" + pet.getNickname());User user = pet.getUser();System.out.println("user信息-" + user.getId() + "-" + user.getName());if(sqlSession != null) {sqlSession.close();}}}
测试效果
3.3.3 注解实现多对 1 映射-应用实例
需求说明: 通过注解的方式来实现下面的多对 1 的映射关系,实现级联查询,完成前面完成 的任务,通过 User-->Pet 也可 Pet->User , 在实际开发中推荐使用配置方式来做
(1)创建 UserMapperAnnotation.java
package com.mapper;import com.entity.User;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;/*** UserMapperAnnotation:以注解的方式来配置多对一*/
public interface UserMapperAnnotation {//通过id获取User对象@Select("SELECT * FROM `mybatis_user` WHERE `id` = #{id}")@Results({@Result(id = true, property = "id", column = "id"),@Result(property = "name", column = "name"),//这里请小伙伴注意,pets属性对应的是集合@Result(property = "pets",column = "id",many = @Many(select = "com.mapper.PetMapperAnnotation.getPetByUserId"))})public User getUserById(Integer id);
}
(2)创建 PetMapperAnnotation.java
package com.mapper;import com.entity.Pet;
import org.apache.ibatis.annotations.*;import java.util.List;public interface PetMapperAnnotation {//通过User的id来获取pet对象,可能有多个,因此使用List接收//id = "PetResultMap" 就是给我们的Results[Result Map] 指定一个名字//,目的是为了后面复用@Select("SELECT * FROM `mybatis_pet` WHERE `user_id` = #{userId}")@Results(id = "PetResultMap", value = {@Result(id = true, property = "id", column = "id"),@Result(property = "nickname", column = "nickname"),@Result(property = "user",column = "user_id",one = @One(select = "com.mapper.UserMapperAnnotation.getUserById"))})public List<Pet> getPetByUserId(Integer userId);//通过pet的id获取Pet对象, 同时会查询到pet对象关联的user对象/*** @ResultMap("PetResultMap") 使用/引用我们上面定义的 Results[ResultMap]*/@Select("SELECT * FROM `mybatis_pet` WHERE `id` = #{id}")@ResultMap("PetResultMap")public Pet getPetById(Integer id);
}
(3)创建 UserMapperAnnotationTest.java 完成测试
package com.mapper;import com.entity.Pet;
import com.entity.User;
import com.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;import java.util.List;public class UserMapperAnnotationTest {//属性private SqlSession sqlSession;private UserMapperAnnotation userMapperAnnotation;//初始化@Beforepublic void init() {//获取到sqlSessionsqlSession = MyBatisUtils.getSqlSession();userMapperAnnotation = sqlSession.getMapper(UserMapperAnnotation.class);}@Testpublic void getUserById() {User user = userMapperAnnotation.getUserById(2);System.out.println("user信息-" + user.getId() + "-" + user.getName());List<Pet> pets = user.getPets();for (Pet pet : pets) {System.out.println("宠物信息-" + pet.getId() + "-" + pet.getNickname());}if(sqlSession != null) {sqlSession.close();}}
}
测试效果
4 缓存-提高检索效率的利器
4.1 缓存-官方文档
文档地址:mybatis – MyBatis 3 | XML 映射器
4.2 一级缓存
4.2.1 基本介绍
(1)默认情况下,mybatis 是启用一级缓存的/本地缓存/local Cache,它是 SqlSession 级别的
(2)同一个 SqlSession 接口对象调用了相同的 select 语句,会直接从缓存里面获取,而不是再去查询数据库
(3)一级缓存原理图
4.2.2 一级缓存快速入门
需求: 当我们第 1 次查询 id=1 的 Monster 后,再次查询 id=1 的 monster 对象,就会直接 从一级缓存获取,不会再次发出 sql
(1)创建新module: mybatis_cache , 必要的文件和配置直接从mybatis_quickstart module 拷贝即可,需要拷贝的文件和配置如图
(2)使用 MonsterMapperTest.java , 运行 getMonsterById() 通过查看日志输出, 结论我们多次运行,总是会发出 SQL
(3)修改MonsterMapperTest.java, 增加测试方法, 测试一级缓存的基本使用(无需配置,因为默认的就是一级缓存)
//测试一级缓存
@Test
public void level1CacheTest() {//查询id=1的monsterMonster monster = monsterMapper.getMonsterById(1);System.out.println("monster=" + monster);//再次查询id=1的monster//当我们再次查询 id=1的Monster时,直接从一级缓存获取,不会再次发出sqlSystem.out.println("--一级缓存默认是打开的,当你再次查询相同的id时, 不会再发出sql----");Monster monster2 = monsterMapper.getMonsterById(1);System.out.println("monster2=" + monster2);if (sqlSession != null) {sqlSession.close();}
}
4.2.3 一级缓存失效分析
(1)关闭 sqlSession 会话后, 再次查询,会到数据库查询
(2)当执行 sqlSession.clearCache() 会使一级缓存失效
(3)当对同一个 monster 修改(update),该对象在一级缓存会失效
4.3 二级缓存
4.3.1 基本介绍
(1)二级缓存和一级缓存都是为了提高检索效率的技术
(2)最大的区别就是作用域的范围不一样,一级缓存的作用域是 sqlSession 会话级别,在一次 会话有效,而二级缓存作用域是全局范围,针对不同的会话都有效
(3)二级缓存原理图
4.3.2 二级缓存快速入门
(1)mybatis-config.xml 配置中开启二级缓存
<configuration><properties resource="jdbc.properties"/><settings><!--配置MyBatis自带的日志输出-查看原生的sql--><setting name="logImpl" value="STDOUT_LOGGING"/><!--1、全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 可以理解这是一个总开关2、默认就是: true--><setting name="cacheEnabled" value="true"/></settings>
。。。。。
</configuration>
public class Monster implements Serializable {
(3)在对应的 XxxMapper.xml 中设置二级缓存的策略
<mapper namespace="com.mapper.MonsterMapper"><!--1、配置二级缓存: 是mybatis自带2、eviction="FIFO" – 先进先出:按对象进入缓存的顺序来移除它们。默认LRU,最近最少使用。可选属性还有"SOFT"软引用,"WEAK"弱引用3. flushInterval 刷新间隔 是毫秒单位 60000 表示 60s4. size="512": 引用数目, 属性可以被设置为任意正整数, 默认10245. readOnly="true": (只读)属性可以被设置为 true 或 false: 如果我们只是用于读操作,建议设置成 true, 这样可以提示效率, 如果有修改操作,设置成 false, 默认就是false--><cache eviction="FIFO" flushInterval="60000"size="512" readOnly="true"/>。。。。。。。
</mapper>
(4)修改 MonsterMapperTest.java , 完成测试
//测试二级缓存的使用
@Test
public void level2CacheTest() {//查询id=1的monsterMonster monster = monsterMapper.getMonsterById(1);System.out.println("monster=" + monster);//这里关闭sqlSessionif (sqlSession != null) {sqlSession.close();}//重新获取sqlSessionsqlSession = MyBatisUtils.getSqlSession();//重新获取了monsterMappermonsterMapper = sqlSession.getMapper(MonsterMapper.class);//再次查询id=3的monsterSystem.out.println("--虽然前面关闭了sqlSession,因为配置二级缓存, " +"当你再次查询相同的id时, 依然不会再发出sql, 而是从二级缓存获取数据----");Monster monster2 = monsterMapper.getMonsterById(1);System.out.println("monster2=" + monster2);if (sqlSession != null) {sqlSession.close();}
}
测试效果
4.3.3 注意事项和使用陷阱
(1)理解二级缓存策略的参数
<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/
上面的配置意思如下:
创建了 FIFO 的策略,每隔 30 秒刷新一次,最多存放 360 个对象而且返回的对象被认为是 只读的。
- eviction:缓存的回收策略
- flushInterval:时间间隔,单位是毫秒
- size:引用数目,内存大就多配置点,要记住你缓存的对象数目和你运行环境的可用内存 资源数目。默认值是 1024
- readOnly:true,只读
(2)四大策略
- LRU – 最近最少使用的:移除最长时间不被使用的对象,它是默认
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
(3)如何禁用二级缓存
1) 在 mybatis-config.xml
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 默认就是 true-->
<setting name="cacheEnabled" value="false"/>
</settings>
2)在 mapper\MonsterMapper.xml 中把下面这一行注释掉
<!--<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>-->
3) 或者更加细粒度的, 在配置方法上指定
设置 useCache=false 可以禁用当前 select 语句的二级缓存,即每次查询都会发出 sql 去查询, 默认情况是 true,即该 sql 使用二级缓存。 注意:一般我们不需要去修改,使用默认的即可
(4)mybatis 刷新二级缓存的设置
<update id="updateMonster" parameterType="Monster" flushCache="true">UPDATE mybatis_monster SET NAME=#{name},age=#{age} WHERE id=#{id}
</update>
insert、update、delete 操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读 默认为 true,默认情况下为 true 即刷新缓存,一般不用修改。
(5)不会出现一级缓存和二级缓存中有同一个数据。因为二级缓存(数据)是在一级缓存关闭 之后才有的
4.4 Mybatis 的一级缓存和二级缓存执行顺序
缓存执行顺序是:二级缓存-->一级缓存-->数据库
说明:先看二级缓存有没有数据,如果没有再看一级缓存,一级缓存也没有数据,才会连接数据库。当我们关闭一级缓存的时候,如果配置了二级缓存,那么一级缓存的数据,会放入到二级缓存
小实验,测试缓存执行顺序
(1)修改 MonsterMapperTest.java,增加如下方法
//演示二级缓存->一级缓存->DB执行的顺序
@Test
public void cacheSeqTest() {System.out.println("查询第1次");//DB, 会发出SQL, 分析cache hit ratio 0.0Monster monster1 = monsterMapper.getMonsterById(1);System.out.println(monster1);//这里关闭sqlSession, 一级缓存数据没有//当我们关闭一级缓存的时候,如果你配置二级缓存,那么一级缓存的数据,会放入到二级缓存sqlSession.close();sqlSession = MyBatisUtils.getSqlSession();monsterMapper = sqlSession.getMapper(MonsterMapper.class);System.out.println("查询第2次");//从二级缓存获取id=1 monster , 就不会发出SQL, 分析cache hit ratio 0.5Monster monster2 = monsterMapper.getMonsterById(1);System.out.println(monster2);System.out.println("查询第3次");//从二级缓存获取id=1 monster, 不会发出SQL, 分析cache hit ratio 0.6666Monster monster3 = monsterMapper.getMonsterById(1);System.out.println(monster3);if (sqlSession != null) {sqlSession.close();}System.out.println("操作成功");}
(2)运行结果:
4.5 EhCache 缓存
4.5.1 基本介绍
(1)配置文档(官方解释): https://www.cnblogs.com/zqyanywn/p/10861103.html
(2)文档说明(通俗解释): https://www.taobye.com/f/view-11-23.html
(3)EhCache 是一个纯 Java 的缓存框架,具有快速、精干等特点
(4)MyBatis 有自己默认的二级缓存(前面我们已经使用过了),但是在实际项目中,往往使用的是更加专业的第三方缓存产品 作为 MyBatis 的二级缓存,EhCache 就是非常优秀的缓存产品
4.5.2 配置和使用 EhCache
(1)加入相关依赖, 修改 mybatis_cache\pom.xml
<dependencies><!--引入ehcache核心库/jar--><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache-core</artifactId><version>2.6.11</version></dependency><!--引入需要使用的slf4j--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><!--引入mybatis整合ehcache库/jar--><dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.2.1</version></dependency>
</dependencies>
(2)mybatis-config.xml 仍然打开二级缓存
<settings><!--1、全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 可以理解这是一个总开关2、默认就是: true--><setting name="cacheEnabled" value="true"/>
</settings>
(3)加入 mybatis_cache\src\main\resources\ehcache.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache><!--diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:user.home – 用户主目录user.dir – 用户当前工作目录java.io.tmpdir – 默认临时文件路径--><diskStore path="java.io.tmpdir/Tmp_EhCache"/><!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。--><!--name:缓存名称。maxElementsInMemory:缓存最大数目maxElementsOnDisk:硬盘最大缓存个数。eternal:对象是否永久有效,一但设置了,timeout将不起作用。overflowToDisk:是否保存到磁盘,当系统宕机时timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。可理解为: TTI用于设置对象在cache中的最大闲置时间,就是 在一直不访问这个对象的前提下,这个对象可以在cache中的存活时间。timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。可理解为: TTL用于设置对象在cache中的最大存活时间,就是 无论对象访问或是不访问(闲置),这个对象在cache中的存活时间diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。clearOnFlush:内存数量最大时是否清除。memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。FIFO,first in first out,这个是大家最熟的,先进先出。LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。--><defaultCacheeternal="false"maxElementsInMemory="10000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="259200"memoryStoreEvictionPolicy="LRU"/><cachename="cloud_user"eternal="false"maxElementsInMemory="5000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="1800"memoryStoreEvictionPolicy="LRU"/></ehcache>
(4)在 XxxMapper.xml 中启用 EhCache , 记得把原来 MyBatis 自带的缓存配置注销
<!--配置/启用ehcache-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(5)修改 MonsterMapperTest.java , 增加测试方法, 完成测试
//测试ehCache级缓存
@Test
public void ehCacheTest() {//查询id=1的monsterMonster monster = monsterMapper.getMonsterById(1);//会发出SQL, 到db查询System.out.println("monster=" + monster);//这里关闭sqlSession, 一级缓存[数据]失效.=> 将数据放入到二级缓存 (ehcache)if (sqlSession != null) {sqlSession.close();}//重新获取sqlSessionsqlSession = MyBatisUtils.getSqlSession();//重新获取了monsterMappermonsterMapper = sqlSession.getMapper(MonsterMapper.class);//再次查询id=1的monsterSystem.out.println("--虽然前面关闭了sqlSession,因为配置二级缓存(ehcache), " +"当你再次查询相同的id时, 不会再发出sql, 而是从二级缓存(ehcache)获取数据----");Monster monster2 = monsterMapper.getMonsterById(1);System.out.println("monster2=" + monster2);//再次查询id=3的monster, 仍然到二级缓存(ehcache), 获取数据, 不会发出sqlMonster monster3 = monsterMapper.getMonsterById(1);System.out.println("monster3=" + monster3);if (sqlSession != null) {sqlSession.close();}}
测试效果
4.5.3 EhCache 缓存-细节说明
如何理解 EhCache 和 MyBatis 缓存的关系
(1)MyBatis 提供了一个接口 Cache【如右图,找到 org.apache.ibatis.cache.Cache ,关联源 码包就可以看到 Cache 接口】
(2)只要实现了该 Cache 接口,就可以作为二级缓存产品和 MyBatis 整合使用,Ehcache 就是实现了该接口
(3)MyBatis 默认情况(即一级缓存)是使用的 PerpetualCache 类实现 Cache 接口的
(4)当我们使用了 Ehcahce 后,就是 EhcacheCache 类实现 Cache 接口的.
(5)我们看一下源码,发现缓存的本质就是 Map<Object,Object>
相关文章:

MyBatis框架学习笔记(四):动态SQL语句、映射关系和缓存
1 动态 SQL 语句-更复杂的查询业务需求 1.1 动态 SQL-官方文档 (1)文档地址: mybatis – MyBatis 3 | 动态 SQL (2)为什么需要动态 SQL 动态 SQL 是 MyBatis 的强大特性之一 使用 JDBC 或其它类似的框架,根据不同条…...

【C++PythonJava】字符处理详细解读_字符_ASCLL码_字母数字转换_算法竞赛_开发语言
文章目录 Beginning1)ASCLL 码2)大小比较2)判断数字字符3)字符、数字间的相互转换End Beginning 在 C 中,字符和整数有着密不可分的关系。原因就是在计算机中,字符是以一种较 ASCLL 码的整数存储的。自然&…...
人像视频淡入淡出效果的灵敏检验方法
在视频中经常会有淡入淡出的效果,这可能导致人脸检测在实际人已经离开画面之后仍然触发,特别是在使用基于像素强度变化的检测算法时。为了更精确地裁剪视频,你可以尝试以下几种方法: 使用更复杂的人脸检测模型: 有些…...

Unity UGUI Image Maskable
在Unity的UGUI系统中,Maskable属性用于控制UI元素是否受到父级遮罩组件的影响。以下是关于这个属性的详细说明和如何使用: Maskable属性 Maskable属性: 当你在GameObject上添加一个Image组件(比如UI面板或按钮)时&…...

SpringCloud | 单体商城项目拆分(微服务)
为什么要进行微服务拆分? 在平常的商城项目中,我们一般的项目结构模块都是将各种业务放在同一个项目文件夹,比如像: 用户,购物车,商品,订单,支付等业务都是放在一起,这样…...
uniapp 如何实现路由拦截,路由守卫
uniapp框架的全局文件:page.json全局文件,官网链接 背景: 通过封装 UniApp 的路由方法,并在封装方法中添加自定义逻辑,可以实现类似 Vue Router 的路由守卫功能。 在 UniApp 框架中,不像 Vue Router 直接支…...

人工智能算法工程师(中级)课程13-神经网络的优化与设计之梯度问题及优化与代码详解
大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(中级)课程13-神经网络的优化与设计之梯度问题及优化与代码详解。 文章目录 一、引言二、梯度问题1. 梯度爆炸梯度爆炸的概念梯度爆炸的原因梯度爆炸的解决方案 2. 梯度消失梯度消失的概念梯度…...
Qt/QML学习-ComboBox
QML学习 ComboBox例程视频讲解代码 main.qml import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15Window {width: 640height: 480visible: truetitle: qsTr("ComboBox")ComboBox {id: comboBox// 列表项数据模型model: ListModel {List…...

微服务实战系列之玩转Docker(一)
前言 话说计算机的“小型化”发展,历经了大型机、中型机直至微型机,贯穿了整个20世纪的下半叶。同样,伴随着计算机的各个发展阶段,如何做到“资源共享、资源节约”,也一直是一代又一代计算机人的不懈追求和历史使命。今…...
Java中常见的语法糖
文章目录 概览泛型增强for循环自动装箱与拆箱字符串拼接枚举类型可变参数内部类try-with-resourcesLambda表达式 概览 语法糖是指编程语言中的一种语法结构,它们并不提供新的功能,而是为了让代码更易读、更易写而设计的。语法糖使得某些常见的编程模式或…...

数据库使用SSL加密连接
简介 数据库开通SSL加密连接是确保数据传输过程中安全性的关键措施,它通过加密数据、验证服务器身份、保护敏感信息、维护数据完整性和可靠性,同时满足行业标准和法规要求,进而提升用户体验和信任度,为企业的数据安全和业务连续性…...

华为OD算法题汇总
60、计算网络信号 题目 网络信号经过传递会逐层衰减,且遇到阻隔物无法直接穿透,在此情况下需要计算某个位置的网络信号值。注意:网络信号可以绕过阻隔物 array[m][n],二维数组代表网格地图 array[i][j]0,代表i行j列是空旷位置 a…...
服务器的rabbitmq的guest账号登不进去
要配置 RabbitMQ 允许 guest 账号从非 localhost 地址登录,需要执行以下步骤: 编辑 RabbitMQ 配置文件: 打开 RabbitMQ 的配置文件,通常位于 /etc/rabbitmq/rabbitmq.conf 或者 /etc/rabbitmq/rabbitmq-env.conf。如果这些文件不存…...

决策树(ID3,C4.5,C5.0,CART算法)以及条件推理决策树R语言实现
### 10.2.1 ID3算法基本原理 ### mtcars2 <- within(mtcars[,c(cyl,vs,am,gear)], {am <- factor(am, labels c("automatic", "manual"))vs <- factor(vs, labels c("V", "S"))cyl <- ordered(cyl)gear <- ordered…...

文心一言《使用手册》,文心一言怎么用?
一、认识文心一言 (一)什么是文心一言 文心一言是百度研发的 人工智能大语言模型产品,能够通过上一句话,预测生成下一段话。 任何人都可以通过输入【指令】和文心一言进行对话互动、提出问题或要求,让文心一言高效地…...

Spring Boot集成qwen:0.5b实现对话功能
1.什么是qwen:0.5b? 模型介绍: Qwen1.5是阿里云推出的一系列大型语言模型。 Qwen是阿里云推出的一系列基于Transformer的大型语言模型,在大量数据(包括网页文本、书籍、代码等)进行了预训练。 硬件要求:…...
GreenDao实现原理
GreenDao 是一款针对 Android 平台优化的轻量级对象关系映射 (ORM) 框架,它将 Java 对象映射到 SQLite 数据库,以简化数据持久化操作。GreenDao 的主要优点包括高性能、低内存占用、易于使用以及对数据库加密的支持。 以下是基于源码的 GreenDao 实现原…...

Perl语言之数组
Perl数组可以存储多个标量,并且标量数据类型可以不同。 数组变量以开头。访问与定义格式如下: #! /usr/bin/perl arr("asdfasd",2,23.56,a); print "输出所有:arr\n"; print "arr[0]$arr[0]\n"; #输出指定下标 print…...
写材料word和PPT
一、WORD 1、写内容 2、参考GPT改:内容、逻辑结构、语句 3、查标题及其标号 4、修改格式:仿宋 、正文统一为小三,标题三号,1.5倍行距,加页码。 采用VBA代码自动修改,不知为何标题无法修改字体 Sub 插入页…...
Centos---命令详解 vi 系统服务 网络
目录 一、CentOS vi命令详解 二、CentOS系统服务命令 三、CentOS权限管理命令: 四、CentOS网络管理命令介绍: 一、CentOS vi命令详解 Vi是一款强大的文本编辑器,在CentOS中广泛使用。以下是Vi编辑器的一些常用命令: 1. 打开…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...