本人发布的一系列资料都是学习备份使用,以及方便日后复习,随着技术的不断提升,每个文章都会持续添加内容,或者是扩展文章,本是秉承着分享的心态,互相学习,互相提升,也希望提出更好的意见,以及学习思路,欢迎留言,欢迎评论,谢谢!!
/**
* 业务需求: 根据ID查询数据
* 参数: id = 1
*/
@Test
public void test02(){
int id = 1;
User user = userMapper.findUserById(id);
System.out.println(user);
}
User findUserById(int id);
<!--根据id查询用户数据
语法: 动态获取数据 #{属性名称}
#号效果: 有预编译的效果 防止Sql注入攻击问题
所以以后取值使用#号
-->
<select id="findUserById" resultType="com.jt.pojo.User">
select * from demo_user where id= #{id}
</select>
/**
* 实现用户入库操作
*/
@Test
public void saveUser(){
User user = new User();
user.setName("元旦快乐").setAge(2022).setSex("女");
//如果需要返回值 则int接收
int rows = userMapper.saveUser(user);
//如果不需要返回值 则void标识
//userMapper.saveUser(user);
System.out.println("入库成功:影响:"+rows+"行");
}
int saveUser(User user);
<!--
规则:
1.如果接口方法中,有int类型的返回值,则入库之后,mybatis自动回传数据
2.如果接口中传递的是POJO对象.则通过#{属性}取值
大小问题:
1.windows系统中程序运行不区分大小写.
2.Linux系统 严格区分大小写.
3.程序猿口头禅: 我在我本机上没问题呀!!!!
-->
<insert id="saveUser">
insert into demo_user(id,name,age,sex) values (null,#{name},#{age},#{sex})
</insert>
/**
* 3.修改操作 将name="元旦快乐" 改为age=17,sex=男
*/
@Test
public void updateUser(){
User user = new User();
user.setName("元旦快乐").setAge(17).setSex("男");
userMapper.updateUser(user);
System.out.println("修改操作成功!!!");
}
//4.删除操作 将name="春节快乐"的数据删除
@Test
public void deleteUser(){
String name = "春节快乐";
userMapper.deleteUser(name);
System.out.println("删除成功!!!");
}
void updateUser(User user);
void deleteUser(String name);
<update id="updateUser">
update demo_user set age=#{age}, sex=#{sex}
where name=#{name}
</update>
<delete id="deleteUser">
delete from demo_user where name=#{name}
</delete>
/**
* 业务: 查询age> 18岁 and age <100的用户.
* 知识点:
* 1.mybatis的参数可以是基本类型或者字符串.
* 2.如果遇到多个参数,应该使用对象(POJO)进行封装.
* 3.如果通过pojo封装不方便.则使用功能最为强大的Map进行封装
* 4.Mybatis的接口方法中只允许传递单值
*/
@Test
public void testFindUserByAge(){
Map<String,Object> map = new HashMap<>();
map.put("minAge",18);
map.put("maxAge",100);
List<User> userList = userMapper.findUserByAge(map);
System.out.println(userList);
}
List<User> findUserByAge(Map<String, Object> map);
<!--
知识点1: 通过 #{参数名称/对象中的属性/Map中的key}
知识点2: xml文件中有些字符需要转义
> > < <
& &
-->
<select id="findUserByAge" resultType="com.jt.pojo.User">
select * from demo_user where age>#{minAge} and age < #{maxAge}
</select>
说明: 根据下列的代码,不能非常直观去了解业务. 能否优化该操作.
List<User> findUserByAge(Map<String, Object> map);
@Test
public void testFindUserByAge2(){
int minAge = 18;
int maxAge = 100;
List<User> userList = userMapper.findUserByAge2(minAge,maxAge);
System.out.println(userList);
}
//如果参数是多值,则需要封装为单值Map
//@Param将参数封装为Map集合
List<User> findUserByAge2(@Param("minAge") int minAge,
@Param("maxAge") int maxAge);
<select id="findUserByAge2" resultType="com.jt.pojo.User">
select * from demo_user where age>#{minAge} and age < #{maxAge}
</select>
/**
* 需求: 查询name中 包含"君"字的数据
* Sql: where name like "%君%"
*/
@Test
public void findByLike(){
String key = "君";
List<User> userList = userMapper.findByLike(key);
System.out.println(userList);
}
List<User> findByLike(String key);
<!--
知识点1: 通过 #{参数名称/对象中的属性/Map中的key}
知识点2: xml文件中有些字符需要转义
> > < <
& &
-->
<select id="findUserByAge" resultType="com.jt.pojo.User">
select * from demo_user where age>#{minAge} and age < #{maxAge}
</select>
其中resultType中的属性com.jt.pojo.User 定义别名.简化其操作.
<select id="findByLike" resultType="com.jt.pojo.User">
select * from demo_user where name like "%"#{key}"%"
</select>
#3.配置Mybatis
mybatis:
#定义别名包
type-aliases-package: com.jt.pojo
<!--Sql标签作用: 抽取公共的部分,可以被其它的Sql引用-->
<sql id="findColumn">
select id,name,age,sex from demo_user
</sql>
<select id="findByLike" resultType="User">
<include refid="findColumn"/> where name like "%"#{key}"%"
</select>
说明: 一般业务逻辑可能会用到array进行数据的封装. 问题: xml映射文件中如何解析数组
格式如下: Integer[] ids = {1,3,4,5,6};
/**
* 业务说明: 集合参数的用法
* 需求: 查询id=1,3,4,5,6的数据
* Sql: where id in (1,3,4,5,6)
*/
@Test
public void findIn(){
Integer[] ids = {1,3,4,5,6};
List<User> list = userMapper.findIn(ids);
System.out.println(list);
}
List<User> findIn(Integer[] ids);
<!--
需求: Integer[] ids 中的数据一个一个取出来.拼接Sql
知识点: mybatis中如何遍历数组
foreach标签用法:
1. collection:需要遍历的集合的类型
1.1 数组类型 关键字:array
1.2 list类型 关键字:list
1.3 map类型 关键字:Map中的key
2. open/close 循环开始和结束标签,一般自己写
3. item 遍历数据的变量名称
4. separator 参数之间的分隔符
-->
<select id="findIn" resultType="User">
select * from demo_user where id in (
<foreach collection="array" item="id" separator=",">
#{id}
</foreach>
)
</select>
@Test
public void findInMap(){
//准备2个集合,测试map集合封装
Integer[] ids1 = {1,3,4,5,6};
Integer[] ids2 = {1,3,4,5,6};
List<User> list = userMapper.findInMap(ids1,ids2);
System.out.println(list);
}
List<User> findInMap(@Param("ids1") Integer[] ids1,
@Param("ids2") Integer[] ids2);
<!--参数传递是Map集合,所以关键字使用key-ids1 -->
<select id="findInMap" resultType="User">
select * from demo_user where id in (
<foreach collection="ids1" item="id" separator=",">
#{id}
</foreach>
)
</select>
package com.jt;
import com.jt.mapper.UserMapper2;
import com.jt.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class TestMybatis2 {
@Autowired
private UserMapper2 userMapper;
/**
* 需求: 根据用户不为null的属性充当where条件
*/
@Test
public void testSqlWhere(){
User user = new User();
user.setAge(3000).setSex("男");
List<User> userList = userMapper.findSqlWhere(user);
System.out.println(userList);
}
}
@Mapper //将接口的代理对象交给Spring容器管理
public interface UserMapper2 {
List<User> findSqlWhere(User user);
}
<?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.jt.mapper.UserMapper2">
<!--
问题说明: 前端数据传递时可能会有null数据.
如果数据为null则不能充当where条件
解决方案: 动态Sql实现
语法:
<if test="判断条件"> id = #{id}</if>
true: 则拼接标签内容
false: 则不拼接标签内容
<where>标签: 去除where后边多余的and/or
-->
<select id="findSqlWhere" resultType="User">
select * from demo_user
<where>
<if test="id != null"> id = #{id}</if>
<if test="name !=null">and name = #{name}</if>
<if test="age !=null"> and age = #{age}</if>
<if test="sex !=null"> and sex = #{sex}</if>
</where>
</select>
</mapper>
/**
* 需求: 修改id=1 的数据 name="北极熊",age=4000 sex="男"
*/
@Test
public void testUpdateUser(){
User user = new User();
user.setId(1).setAge(5000);
userMapper.updateUser(user);
System.out.println("更新成功!!!");
}
void updateUser(User user);
<!--根据对象中不为null的属性 当做set条件
语法: set标签 去除多余1个,号
-->
<update id="updateUser">
update demo_user
<set>
<if test="name !=null">name=#{name},</if>
<if test="age !=null"> age=#{age},</if>
<if test="sex !=null"> sex=#{sex},</if>
</set>
where id = #{id}
</update>
/**
* 如果name有值,则根据name查询.
* 如果name没有值,则根据age查询.
* 如果name/age都没有值,则根据sex查询
*/
@Test
public void testChoose(){
User user = new User();
user.setSex("男");
List<User> userList = userMapper.findChoose(user);
System.out.println(userList);
}
List<User> findChoose(User user);
<!--
* 如果name有值,则根据name查询.
* 如果name没有值,则根据age查询.
* 如果name/age都没有值,则根据sex查询
语法类似: if->else-if->else
-->
<select id="findChoose" resultType="User">
select * from demo_user
<where>
<choose>
<when test="name !=null"> name=#{name}</when>
<when test="age !=null"> age = #{age}</when>
<!--必须保证sex必须有值 -->
<otherwise>sex=#{sex}</otherwise>
</choose>
</where>
</select>
package com.jt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Dog implements Serializable {
private Integer dogId;
private String dogName;
private Integer age;
}
1.经过demo_user测试,发现如果字段名称与对象属性的名称一致,Mybatis可以实现自动化的映射
2.如果遇到字段名称与属性的名称不一致的现象,则mybatis如何映射!!!
package com.jt;
import com.jt.mapper.DogMapper;
import com.jt.pojo.Dog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class TestMybatis3 {
@Autowired
private DogMapper dogMapper;
@Test
public void testFindAll(){
List<Dog> dogList = dogMapper.findAll();
System.out.println(dogList);
}
}
@Mapper
public interface DogMapper {
List<Dog> findAll();
}
<?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.jt.mapper.DogMapper">
<!--
规则:
1.如果数据库中的表字段名称与POJO属性的名称不一致
则mybatis无法自动完成映射.
2.Mybatis提供了一个属性resultMap(使用最多的).
支持用户手动映射.
-->
<select id="findAll" resultMap="dogRM">
select * from dog
</select>
<!--
属性说明: id="唯一标识,不能重复"
type="映射的POJO对象的类型"
简化说明: 如果字段名称与属性名称一致则可以省略
autoMapping="true" 开启自动映射
-->
<resultMap id="dogRM" type="Dog" autoMapping="true">
<!--1.标识主键-->
<id column="dog_id" property="dogId"/>
<!--2.映射结果集-->
<result column="dog_name" property="dogName"/>
<!--<result column="age" property="age"/>-->
</resultMap>
</mapper>
Mybatis中的结果集的字段名称如果与属性的名称满足驼峰映射的规则. 如果开启驼峰映射.,则可以实现自动化的映射.
字段: dog_id, dog_name
属性: dogId, dogName 满足驼峰规则.
配置信息:
#3.配置Mybatis
mybatis:
#定义别名包
type-aliases-package: com.jt.pojo
#将所有的映射文件全部加载
mapper-locations: classpath:/mappers/*.xml
#开启驼峰映射
configuration:
map-underscore-to-camel-case: true
思路:看问题从一头出发看向另一头
表名 dept
字段: dept_id, int 主键 自增
dept_name, varchar(40)
表名 emp
字段: emp_id, int 主键 自增
emp_name, varchar(40)
dept_id int
表关系: 一个员工对应一个部门.
需求: 将部门信息与员工信息绑定.
如图所示:
package com.jt;
import com.jt.mapper.DeptMapper;
import com.jt.mapper.DogMapper;
import com.jt.mapper.EmpMapper;
import com.jt.pojo.Dog;
import com.jt.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class TestMybatis4 {
@Autowired
private DeptMapper deptMapper;
@Autowired
private EmpMapper empMapper;
//完成一对一测试
@Test
public void testEmp(){
List<Emp> empList = empMapper.findAll();
System.out.println(empList);
}
}
@Mapper
public interface EmpMapper {
List<Emp> findAll();
}
<!--
知识点:
1.如果单表查询首选resultType
2.如果进行关联查询 首选resultMap
3.如果sql的结果集出现了重名字段,则mybatis映射必然报错.
-->
<select id="findAll" resultMap="empRM">
SELECT emp.*,dept.dept_name FROM emp,dept
WHERE emp.dept_id = dept.dept_id
</select>
<!-- 完成一对一封装
目的: 一个员工中封装一个部门对象
语法:
1.association 表示一对一封装
2.property 当前主对象的属性名称
3.javaType 指定属性的类型
-->
<resultMap id="empRM" type="Emp" autoMapping="true">
<!-- 标识主键信息 -->
<id column="emp_id" property="empId"/>
<!--<result column="emp_name" property="empName"/>-->
<!--完成一对一映射-->
<association property="dept" javaType="Dept" autoMapping="true">
<id column="dept_id" property="deptId"/>
</association>
</resultMap>
SELECT emp.*,dept.dept_name FROM
emp
LEFT JOIN
dept
ON emp.dept_id = dept.dept_id
@Test
public void testDept(){
List<Dept> deptList = deptMapper.findAll();
System.out.println(deptList);
}
@Mapper
public interface DeptMapper {
List<Dept> findAll();
}
<mapper namespace="com.jt.mapper.DeptMapper">
<select id="findAll" resultMap="deptRM">
SELECT dept.*,emp.emp_id,emp.emp_name
FROM dept,emp
WHERE dept.dept_id=emp.dept_id
</select>
<!--
一对多封装:
1.collection: 封装集合类型
2.ofType: 指定集合内部(泛型)的对象类型
-->
<resultMap id="deptRM" type="Dept" autoMapping="true">
<!--主键必须标识-->
<id column="dept_id" property="deptId"/>
<!--一对多封装-->
<collection property="emps" ofType="Emp" autoMapping="true">
<id column="emp_id" property="empId"/>
</collection>
</resultMap>
</mapper>
//鸡肋: 1.大公司一般不用, 2.只适用于单表操作.多表操作必须写映射文件
// 注解和映射文件二选一
@Select("select * from dept")
List<Dept> selectAll();
@Insert("insert into dept values (null,#{deptName})")
void saveDept(Dept dept);
如果有大量相同的请求查询数据库,则数据库需要执行多次重复的sql,那么并发压力高,查询效率低. 如果引入缓存机制,则可以极大的提升用户的查询的效率
说明: 在同一个SqlSession内,实现数据库的共享.
解释: 用户使用同一个SqlSession时,进行多次数据库的查询,由于一级缓存的存在.所以数据库只查询一次.
开关状态: 一级缓存Mybatis默认开启.无需配置.
package com.jt;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@SpringBootTest
public class TestMybatisCache5 {
@Autowired
private UserMapper userMapper; //底层实现中包含了SqlSession
/**
* 测试:mybatis的一级缓存, SqlSession级别
* SpringBoot测试说明:
* SpringBoot中用户在使用userMapper接口时,用户每调用一次.
* SpringBoot就会新创建一个SqlSession.
* 如何解决多个SqlSession的问题?
* 解决方案:
* 利用@Transactional的事务的注解,将多个SqlSession控制为一个.
*/
@Test
@Transactional //事务的注解
public void testCache1(){
List<User> list1 = userMapper.findAll(); //sqlSession1
List<User> list2 = userMapper.findAll(); //sqlSession2
List<User> list3 = userMapper.findAll(); //SqlSession3
}
}
说明: 由同一个SqlSessionFactory(类比:链接池)生产的SqlSession(类比:数据库链接),可以实现数据共享
解释说明: 由同一个SqlSessionFactory生产多个SqlSession(多线程).可以在多线程的条件下,可以实现数据共享.
默认开关: 二级缓存默认条件下是开启的. 但是需要指定哪里使用二级缓存
/**
* 测试二级缓存
* 总结: 多线程条件下共享数据,要求数据必须序列化.
*/
@Test
public void testCache2(){
//第一次查询数据库获取的list1的集合对象,该对象需要保存到缓存中,为了后续线程使用,该对象必须序列化
List<User> list1 = userMapper.findAll(); //sqlSession1 线程A
//第二个线程查询数据.有二级缓存的存在.所以从缓存中获取数据.所以直接反序列化该对象获取结果.
List<User> list2 = userMapper.findAll(); //sqlSession2 线程B
//第三个线程查询数据,所以直接反序列化该对象获取结果.
List<User> list3 = userMapper.findAll(); //SqlSession3 线程C
}
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 一级缓存的作用域是同一个SqlSession,在第一个sqlSession执行相同的sql语句后结果放在内存中,第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
本地缓存不能被关闭,可以调用clearCache()来清空本地缓存,或者改变缓存的作用域
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。
如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
重点知识: 1.了解SqlSessionFactory/SqlSession如何创建的!!!
package com.jt;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class MybatisApplicationTests {
/*
* Mysql入门实现步骤:
* 1.编辑mybatis-config.xml核心配置文件
* 1.1执行数据源配置
* 2.编辑POJO实体对象.要求与数据库表中的字段一一对应
* 3.编辑Mapper接口. 添加接口方法
* 4.编辑接口的实现类(配置文件方式) 要求namespace id resultType
* 5.mybatis加载指定的mapper映射文件
* 6.创建SqlSessionFactory工厂对象
* 7.获取SqlSession,开启数据库链接
* 8.获取接口对象(代理对象)
* 9.调用接口方法,获取返回值结果
* 10.关闭sqlSession链接.
* */
@Test
public void testDemo1() throws IOException {
/*创建SqlSessionFactory 类比:链接池 */
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//设计模式1:建造者模式-Builder-.build()
//设计模式2:工厂模式-Factory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
/*从SqlSessionFactory中获取sqlSession*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/*SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();*/
/*获取mapper接口,执行接口方法*/
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
/*Mybatis为接口创建了一个代理对象
知识点: 框架中通过接口调用方法时,如果没有实现类,则为其创建代理对象
代理对象~实现类
*/
System.out.println(userMapper.getClass());
List<User> userList = userMapper.findAll();
System.out.println(userList);
/*执行之后关闭SqlSession*/
sqlSession.close();
}
}
useGeneratedKeys="true" keyColumn="字段" keyProperty="属性名"