POJO
(Plain Old Java Objects,普通老式 Java 对象)
package com.jt.springboot_demo2.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 实体对象:
* 1、类名与表名对应
* 2、属性与字段对应
* 3、pojo属性必须包装类型
* 0与null可区分是否赋值
* 4、实体对象必须有get/set方法
* 5、一般实体对象必须实现序列化接口(规则)
* 原因:数据可能跨平台(跨服务器)传输
* */
@Data //动态生成get/set/toString/equal
@Accessors(chain=true) //重写set方法,实现链式赋值
@NoArgsConstructor //无参构造
@AllArgsConstructor //全参构造
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
//this在运行期有效,代表当前对象 链式加载
public DemoUser setId(Integer id){
this.id = id;
return this ;
}
public void add(){
DemoUser d = new DemoUser();
d
.setAge(21)
.setId(12)
.setName("xy")
.setSex("man");
}
}
MyBatis
持久化:将内存数据保存到磁盘中
持久层:程序通过Dao(Data Access Objects)/Mapper层与数据库进行交互的层级
小结:基于ORM设计实现以对象的方式操作数据库
tips:resources文件下创建多层目录 cn/tedu/mapper
文件关系:
测试类 --加载–> 主配置文件 --sqlsession得到–> mapper接口 --导入接口方法–> mapper实例(执行sql)
主配置文件
mybatis-config.xml配置文件 路径:mybatis/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<!--环境配置标签,default默认环境-->
<environments default="development">
<!--编辑开发环境-->
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--mybatis采用数据库连接池的方式整合数据源-->
<dataSource type="POOLED"> <!--使用池-->
<!--高版本数据库添加cj-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--Mybatis加载Mapper映射文件-->
<mappers>
<mapper resource="mybatis/mappers/UserMapper.xml"/>
</mappers>
</configuration>
测试类
public class TestMybatis {
@Test
public void demo1() throws IOException {
//读取核心配置文件
//指定配置文件地址
String resource = "mybatis/mybatis-config.xml";
//通过io流,加载指定的配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//动态生成SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlsession,类比JDBC链接,执行sql语句最小单位
SqlSession ss = ssf.openSession();
//获取mapper接口,使用接口调用demoUserMapper.xml执行sql(底层JDBC执行sql)
DemoUserMapper m = ss.getMapper(DemoUserMapper.class);
//封装对象,返回结果集
List<DemoUser> userList = m.findAll();
System.out.println(userList);
ss.close();
}
//封装
SqlSessionFactory ssf;
@BeforeEach //@BeforeEach在@Test执行之前执行
public void init() throws IOException {
InputStream s = Resources.getResourceAsStream("mybatis/mybatis-config.xml");
ssf = new SqlSessionFactoryBuilder().build(s);
}
@Test
public void testFindByName(){
//SqlSession sqlSession1 = ssf.openSession(true);
SqlSession sqlSession= ssf.openSession();
DemoUserMapper mapper = sqlSession.getMapper(DemoUserMapper.class);
String s = "王昭君";
List<DemoUser> userList= mapper.findByName(s);
System.out.println(userList);
sqlSession.close();
}
@Test
public void testSaveUser(){
SqlSession sqlSession= ssf.openSession();
DemoUserMapper mapper = sqlSession.getMapper(DemoUserMapper.class);
DemoUser user = new DemoUser(null,"xy",12,"男");
int rows = mapper.saveUser(user); //方法做"更新"操作默认返回修改行数
if(rows>0){
System.out.println("影响行数"+rows);
sqlSession.commit();
}
sqlSession.close(); //更新操作时,需要手动提交事务
}
}
实体对象
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class DemoUser implements Serializable {
private Integer id;
private String name;
private Integer age;
private String sex;
}
mapper接口
mapper持久层下的DemoUserMapper接口
/*
* mybatis中的实现类以xml文件形式存在
* */
public interface DemoUserMapper {
public List<DemoUser> findAll(); //在测试类中调用,在xml中实现
DemoUser findOne(int id);
}
mapper实例
demoUserMapper.xml实例 路径:mybatis/mappers/demoUserMapper.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是mybaits映射文件的唯一标识,与接口对应-->
<mapper namespace="com.jt.mapper.DemoUserMapper">
<!--id 表示接口方法 resultType、parameterType如果是对象,则用该d包路径-->
<select id="findAll" resultType="com.jt.pojo.DemoUser">
select * from demo_user
</select>
<!-- parameterType可省略-->
<select id="findOne" parameterType="int" resultType="com.jt.pojo.DemoUser">
<!--通过#{}获取参数-->
select * from demo_user where id = #{id}
</select>
<!--传递的是对象,可用属性直接使用-->
<select id="findBySA" resultType="com.jt.springboot_demo3_mybatis.pojo.DemoUser">
select * from demo_user where sex = #{sex} and age > #{age}
</select>
<!--执行跟新操作时,返回影响行数-->
<insert id="saveUser">
insert into demo_user value (null,#{name},#{age},#{sex})
</insert>
</mapper>
环境
环境:pom.xml导入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--表示maven坐标-->
<!-- 项目的组ID-->
<groupId>com.jt</groupId>
<!-- 项目名-->
<artifactId>springboot_demo1</artifactId>
<!--项目版本号-->
<version>0.0.1-SNAPSHOT</version>
<name>springboot_demo1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.1</spring-boot.version>
</properties>
<dependencies>
<!--springboot的启动项 web相当于引入mvc框架
思想:开箱即用,只需要引入jar包,简单配置即可使用该功能
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入单元测试的api-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--支持热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--lombok依赖包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type> <!--父级标识-->
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>com.jt.springboot_demo3_mybatis.SpringbootDemo3MybatisApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
多值传递
Mybatis默认支持单值传参,多值要转换为单值
Mybatis多值传参默认采用下标方式获取数据,应封装
封装策略:
1、封装为实体对象,属性只出现一次(只能传一个对象)
2、常用Map集合 Map<String,Object>
3、注解@Param(“key”) Object value (底层封装为Map)
相同类型的数据多值传递
1、array
2、List
3、Map
//测试类传入数组参数
int[] ids = new int[]{1,3,4,5};
int rows = mapper.deleteIds(ids);
//传入List,只需要改collection参数
List list = new ArrayList();
int rows = mapper.deleteList(list);
//传入map,只需要改collection参数
Map map = new HashMap();
map.put("ids",List);
int rows = mapper.deleteM(map);
集合遍历foreach
<!-- collection: 参数是数组,collection="array" list集合,collection="list" map集合,collection = "map中的kay" item:每次遍历的形参变量,与#{}中的参数名一致 open:循环开始标签 close:循环结束标签 index:循环遍历下标(一般不用) separator:循环遍历的分隔符 --> <delete id="deleteIds"> delete form demo_user where id in <foreach collection="array" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>
map批量更新
public void testUpdate1(){ SqlSession sqlSession = ssf.openSession(true); DemoUserMapper mapper = sqlSession.getMapper(DemoUserMapper.class); Map<String,Object> map = new HashMap<>(); String[] arr = {"小乔","大乔","王昭君"}; map.put("names", arr); map.put("age", 18); map.put("sex", "女"); mapper.updateUser(map); sqlSession.close(); }
<update id="updateUser"> update demo_user set age = #{age},sex = #{sex} where name in ( <foreach collection="names" item="name" separator=","> #{name} </foreach> ) </update>
注意事项
#号与$符的区别
对象或map集合传入参数可#{}直接使用
-
#{}以数据为参数,默认有预编译的效果,默认加了" " ,防止sql注入攻击
-
${}以字段名称为参数,直接传值,慎用(写死,不传参数)
转义字符
> >大于< <小于& &与
转义标签体
<![CDATA[ sql语句 ]]> <![CDATA[select * from demo_user where age > #{minage} and age < #{maxage}]]>
SQL
大小写转换快捷键:ctrl+shift+u
like模糊查询
select * from demo_user where name like "%"#{s}"%"
Mybatis优化设置
配置pojo类别名
简化resultType的值
<!--主配置文件,配置别名--><typeAliases> <!--只对某个类有效--> <typeAlias type="cn.jt.pojo.DemoUser" alias="DemoUser"></typeAlias> <!--自动拼接包名--> <package name="cn.jt.pojo"/></typeAliases>
<!--mapper文件,拼接:cn.jt.pojo+DemoUser--> <select id="select02" resultType="DemoUser"> SELECT * FROM DEMO_USER WHERE NAME LIKE "%"#{S}"%" ORDER BY AGE DESC </select>
SQL标签
优势:缩减xml文件大小,代码结构相对简单
弊端:只能抽取公共部分,代码可读性差
<sql id="demo_user_sql"> select * from demo_user </sql> <select id="select02" resultType="DemoUser"> <include refid="demo_user_sql"/>where name like "%"#{s}"%" order by age desc </select>
Mybatis-动态SQL
where、if标签:去除多余and/or
<select id="findWhere" resultType="DemoUser"> select * from demo_user <where> <if test="name != null">name = #{name}</if> <if test="age != null">and age = #{age}</if> <if test="sex != null">and sex = #{sex}</if> </where> </select>
set标签、if标签:去除多余逗号
<update id="updateSet"> update demo_user <set> <if test="name != null">name = #{name},</if> <if test="age != null">and age = #{age},</if> <if test="sex != null">and sex = #{sex}</if> </set> where id = #{id} </update>
choose、when、otherwise标签
不想把全部条件当作if的判断(if-else),只执行一个条件
<select id="choose" resultType="DemoUser"> select * from demo_user where <choose> <when test="name != null">name = #{name}</when> <otherwise>sex = #{sex}</otherwise> </choose> </select>
配置别名
<!--配置别名,主配置文件顺序不可随便修改,在environment前--> <typeAliases> <!--只对某个类有效,别名标签--> <typeAlias type="cn.jt.pojo.DemoUser" alias="DemoUser"></typeAlias> <!--自动拼接包名,别名包--> <package name="cn.jt.pojo"/> </typeAliases>
@Alias("DemoUser")public class DemoUser implements Serializable { private Integer id; private String name; private Integer age; private String sex;}
resultType与resultMap
resultType:单表查询时字段与属性名一致
resultMap:解决字段(结果集)与属性名称不一致的问题
<!--resultType:当结果集中字段名称与pojo中的属性一致时,才能实现自动封装--> <select id="findAll" resultType="Dept"> select * from dept </select>
<select id="findAll" resultMap="deptRM"> select * from dept </select> <!--type简写了配置的包--> <!-- 自定义映射关系 id代表主键(每张表都有一个主键) 1.column代表 结果集 中的字段(结果集可用别名,如多表查询) 2.property代表封装的对象中的属性 result代表主键外的数据 --> <resultMap id="deptRM" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> </resultMap>
*Mybatis关联关系(多表查询)
关联查询效率高于子查询
一对一
关联查询:sql复杂,封装简单
<select id="findAll" resultMap="empRM"> select * from emp left join dept on dept.`dept_id`=emp.`dept_id` </select> <!--字段属性一致可以省略不写,最好保留主键 遇到关联封装,必须全部配置映射关系 autoMapping="true"实现自动映射;条件:多表查询,字段属性一致 --> <!--resultType返回值类型,select里引用resultMap里的type,为返回值类型--> <resultMap id="empRM" type="Emp" autoMapping="true"> <id column="id" property="id"></id> <!--完成一个对象属性的封装 单个对象用association javaType指定属性类型,路径 固定搭配:association、javaType --> <!--association类比resultMap 没有column,改为对象,以下像resultMap一样建立映射关系 property为emp属性,将该属性封装为对象 javaType是封装对象的类型 --> <association property="dept" javaType="cn.jt.pojo.Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> </association> </resultMap>
子查询:sql简单,封装复杂
<!--where条件子查询--> <select id="findAllWHere" resultMap="empRM2"> select * from emp </select> <resultMap id="empRM2" type="Emp" autoMapping="true"> <id column="id" property="id"></id> <!-- column表示子查询的字段信息 select="sql的id"根据column中的数据实现子查询, property="dept" dept是在emp中的属性 column="dept_id" dept_id把两个表的关联起来的字段 将emp中查到的dept_id作为根据,把dept中的dept_id连接,封装为对象,给emp --> <association property="dept" javaType="Dept" column="dept_id" select="findDept"></association> </resultMap><!--子查询--> <select id="findDept" resultMap="deptRM"> select * from dept where dept_id = #{dept_id} </select> <resultMap id="deptRM" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> </resultMap>
一对多
关联查询
1、
<!--一对多--> <select id="findDept" resultMap="listRM"> select d.dept_id,d.dept_name,e.id,e.name,e.age from dept d left join emp e on d.`dept_id`=e.`dept_id` </select> <resultMap id="listRM" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> <!--ofType:封装list集合的泛型对象 ofType的类型是被封装的对象的类型--> <collection property="emps" ofType="cn.jt.pojo.Emp" autoMapping="true"> <id column="id" property="id"></id> </collection> </resultMap>
2、
public class Rights extends BasePojo{ private Integer id; private String name; private Integer parentId; private String path; private Integer level; private List<Rights> children; //不是表格固有属性,一对多:使用list集合}
<!--不出现重名字段,进行重命名 resultMap多表查询--> <select id="getRightsList" resultMap="rightsRM"> SELECT p.id,p.name,p.parent_id,p.path,p.level,p.created,p.updated, c.id c_id,c.name c_name,c.parent_id c_parent_id, c.path c_path,c.level c_level,c.created c_created, c.updated c_updated FROM (SELECT * FROM rights WHERE parent_id=0)p LEFT JOIN rights c ON p.id = c.parent_id </select> <!--对对象Rights与数据库查询字段进行映射、封装--> <resultMap id="rightsRM" type="Rights" autoMapping="true"> <id property="id" column="id"></id> <!--对属性children与数据库查询数据进行映射--> <collection property="children" ofType="Rights"> <id property="id" column="c_id"></id> <result property="name" column="c_name"/> <result property="parentId" column="c_parent_id"/> <result property="path" column="c_path"/> <result property="level" column="c_level"/> <result property="created" column="c_created"/> <result property="updated" column="c_updated"/> </collection>
子查询
<!--子查询--> <select id="getRightsList2" resultMap="childrenRM"> SELECT * FROM rights WHERE parent_id=0 </select> <resultMap id="childrenRM" type="Rights" autoMapping="true"> <id property="id" column="id"></id> <!--column传给子查询的数据--> <collection property="children" ofType="Rights" select="selectRightsByParentId" column="id"></collection> </resultMap> <!--单表查询子数据--> <select id="selectRightsByParentId" resultType="Rights"> SELECT * FROM rights WHERE parent_id=#{id} </select>
驼峰自动映射
关联查询生效需要开启:autoMapping=“true”
最后保留主键:,为以后拓展
<configuration> <!--mybatis的核心配置--> <settings> <!--开启驼峰自动映射规则--> <setting name="mapUnderscoreToCamelCase " value="true"/> </settings><configuration>
Mybatis的缓存机制
降低访问物理设备次数,提高用户响应速度
一级缓存
默认开启,可以在同一个SqlSession对象中,查询相同数据,实现数据共享
规则:同一个SqlSession内部有效
二级缓存
默认开启,需要手动标识
规则:同一个SqlSessionFactory内部有效
在mapper实现类的xml文件中添加
<!--namespace是mybaits映射文件的唯一标识,与接口对应--><mapper namespace="cn.jt.mapper.DeptMapper"> <cache/>
说明:sqlSession查数据后交给一级缓存
sqlSession业务逻辑执行成功并且关闭
二级缓冲才能起作用
增删改会删除一二级缓存
总结
ORM(Object Relational Mapping)对象关系映射
Mybatis是一个优秀的持久层框架,将接口和 Java 的 POJO映射成数据库中的记录。
补充
参数
mybatis利用下标取值,一个参数时,名称任意 #{任意} 但注意开发规范
map或@Param必须用key取值value #{key}
对象传参 #{属性}
动态代理
JDK动态代理
代理者必须实现接口,代理对象是目标对象的实现类或兄弟元素
CGLIB代理
不管是否实现接口接口,代理对象是目标对象的子类
spring自动导入jar包
mapper接口的说明
DemoUserMapper mapper = sqlSession.getMapper(DemoUserMapper.class);
接口不可创建对象,mapper为JDK动态代理对象
调用代理对象与接口功能一致