目录
ORM思想
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换
-
概括: 用对象的方式操作数据库
-
衍生:
-
对象应该与数据库中的表一一映射.
-
对象中的属性应该与表中的字段一一映射.
-
其中的映射应该由程序自动完成.无需人为干预.
-
常规JDBC的弊端
//利用jdbc,完成新增的功能 private static void method2() throws Exception{ //1,注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2,获取数据库的连接 //数据传输协议 数据库的ip 端口号 数据库名 String url = "jdbc:mysql://localhost:3306/cgb2107"; Connection c = DriverManager.getConnection(url,"root","root"); //3,获取传输器 Statement s = c.createStatement(); //4,利用传输器执行 增删改的SQL //executeUpdate()用来执行增删改的SQL,只返回影响行数 int rows = s.executeUpdate( "INSERT INTO emp(ename,job) VALUES('rose','副总')"); //5,释放资源 //r.close();//结果集 s.close();//传输器 c.close();//连接 }
-
弊端
-
无论如何执行都必须获取数据库链接。 链接池: c3p0 druid HK链接池
-
操作sql语句时,步骤繁琐. 不便于学习记忆.
-
资源必须手动关闭.
-
-
优点
-
操作数据库最快的方式就是JDBC. 协议 TCP
-
引出:
Mybatis
-
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射
-
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。(mybatis在内部将JDBC封装).
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
知识整理:
-
持久化:计算机在计算时,数据都在内存中.如果断电则数据清空,所以要求将内存数据保存到磁盘中. 概念.
-
持久层:程序通过Dao/Mapper 与数据库进行交互的层级代码 (Controller层 Service层 Dao/Mapper层) 具体操作.
小结: Mybatis是一个优秀的持久层框架,基于ORM设计思想,实现了以对象的方式操作数据库.
了解:Mybatis的ORM并不完全,只完成了结果集映射,但是Sql需要自己手写.所以也称之为半自动化的ORM映射框架.
Mybatis特点
-
简单易学
-
灵活
-
解除sql与程序代码的耦合
-
提供映射标签,支持对象与数据库的orm字段关系映射
-
提供对象关系映射标签,支持对象关系组建维护
-
提供xml标签,支持编写动态sql
Mybatis入门案例
准备资源:
导入jar包
<!--引入插件lombok 自动的set/get/构造方法插件 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </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>
编辑POJO对象
package com.jt.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.io.Serializable; @Data//1.实现get/set/...方法 @Accessors(chain = true)//2.链式加载--->设置值 @NoArgsConstructor//3.无参构造 @AllArgsConstructor//4.有参构造 public class DemoUser implements Serializable{//5.实现序列化 private Integer id; private String name; private Integer age; private String sex; }
编写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"> <!--编辑开发环境 id是环境的唯一标识符--> <environment id="development"> <!--事务管理器 利用JDBC控制事务--> <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映射文件是有顺序的 通过resource 加载指定xml映射文件 --> <mappers> <mapper resource="mybatis/mappers/demoUserMapper.xml"/> </mappers> </configuration>
编写持久层mapper的接口
package com.jt.mapper; import com.jt.pojo.DemoUser; import java.util.List; /** * 说明: * 1.根据面向接口开发思想需要定义一个Mapper接口 * 2.在接口中可以写方法,谁调用谁实现 * 3.Mybatis中的实现类以xml文件形式存在 */ public interface DemoUserMapper { //查询所有的User列表信息 List<DemoUser> findAll(); DemoUser findOne(int id); }
编写接口实现类的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"> <!--xml映射文件 必须与接口一对一绑定 namespace:指定需要绑定接口的名称,不能重复 --> <mapper namespace="com.jt.mapper.DemoUserMapper"> <!--实现接口中方法 id: 需要与接口中的方法绑定, resultType: 返回值结果对象 规则: sql语句不要添加多余的;号 Oracle数据库不能添加;号 --> <select id="findAll" resultType="com.jt.pojo.DemoUser"> select * from demo_user </select> <!-- parameterType:参数类型 resultType:返回值结果 mybatis中通过 #{} 获取参数 --> <select id="findOne" parameterType="int" resultType="com.jt.pojo.DemoUser" > select * from demo_user where id = #{id}; </select> </mapper>
核心配置文件中配置mybatis关联的映射文件
<!--Mybatis加载Mapper映射文件 mappers映射文件是有顺序的 通过resource 加载指定xml映射文件 --> <mappers> <mapper resource="mybatis/mappers/demoUserMapper.xml"/> </mappers>
编写测试类进行测试
package com.jt; import com.jt.mapper.DemoUserMapper; import com.jt.pojo.DemoUser; 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.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class TestMybatis { @Test public void testDemo1() throws IOException { /*创建SqlSessionFactory*/ //指定配置文件地址 String resource = "mybatis/mybatis-config.xml"; //通过IO流 加载指定的配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //动态生成SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); /*从SqlSessionFactory中获取sqlSession 类比 数据库链接*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*获取mapper接口,执行接口方法*/ DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); //获取数据 List<DemoUser> userList = demoUserMapper.findAll(); System.out.println(userList); //关闭流 sqlSession.close(); } @Test public void findOne() throws IOException { String resource = "mybatis/mybatis-config.xml"; //通过IO流 加载指定的配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //动态生成SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取接口 DemoUserMapper mapper = sqlSession.getMapper(DemoUserMapper.class); int id=1; DemoUser demoUser = mapper.findOne(id); System.out.println(demoUser); sqlSession.close(); } }
Mybatis调用流程
根据ID查询数据
在入门案例中已经实现了,这里分离出来
编写业务接口
public interface DemoUserMapper { //1.查询所有的表数据 public List<DemoUser> findAll(); DemoUser findOne(int id);}
编辑xml映射文件
<!-- parameterType: 参数类型 mybatis中通过 #{} 获取参数 resultType: 返回值结果对象 --> <select id="findOne" parameterType="int" resultType="com.jt.pojo.DemoUser"> select * from demo_user where id = #{id} </select>
编辑测试API
/*** * 需求: 根据ID查询数据库记录 id=1的数据 */ @Test public void testFindOne() throws IOException { //指定配置文件地址 String resource = "mybatis/mybatis-config.xml"; //通过IO流 加载指定的配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //动态生成SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); //获取接口 DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); int id = 1; DemoUser demoUser = demoUserMapper.findOne(id); System.out.println(demoUser); //关闭链接 sqlSession.close(); }
mybatis入门操作
简化Mybatis操作
public class TestMybatis2 { //定义公共的属性 private SqlSessionFactory sqlSessionFactory; @BeforeEach public void init() throws IOException { String resources = "mybatis/mybatis-config.xml"; InputStream inputStream =Resources.getResourceAsStream(resources); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testMybatis(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); List<DemoUser> list = demoUserMapper.findAll(); System.out.println(list); sqlSession.close(); }
@BeforeEach注解说明
该注解的作用是在执行@Test方法前调用. 是测试方法提供的测试API
mybatis参数封装说明
报错说明:
-
规则:
-
mybatis如果遇到多值传参时,默认条件是采用下标的方式获取数据.
-
mybatis天生只支持单值传参,如果遇到多值的问题,则应该将多值封装为单值.
-
常见封装策略
-
封装为实体对象
-
更为常用方式:Map集合
-
如果传递的数据有多个,使用注解 @Param 封装为Map集合
使用方式: List<DemoUser> findSA3(@Param("sex") String sex, @Param("age") int age);
测试案例:
编写测试方法
//1.封装为实体对象 @Test public void find1(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); //编程思想:面向对象 DemoUser csc = new DemoUser(); csc.setAge(18).setSex("女"); List<DemoUser> list = demoUserMapper.findSA(csc); System.out.println(list); sqlSession.close(); } //2.封装为Map集合 @Test public void find2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); Map<String,Object> map = new HashMap<>(); map.put("sex", "女"); map.put("age", 18); List<DemoUser> list = demoUserMapper.findSA2(map); System.out.println(list); sqlSession.close(); } //3.使用注解@Param封装 @Test public void find3(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); String sex = "女"; int age = 18; List<DemoUser> list = demoUserMapper.findSA3(sex,age); System.out.println(list); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { List<DemoUser> findSA(DemoUser csc); List<DemoUser> findSA2(Map<String, Object> map); //利用注解@Param将参数封装为Map集合 List<DemoUser> findSA3(@Param("sex") String sex, @Param("age") int age); }
编写xml映射文件
<!-- 查询sex=女 and age > 18岁 参数: DemoUser user 意图:传递属性的 规则: 如果传递的参数是对象,则通过#{属性} 可以直接获取数据. --> <select id="findBySA" resultType="com.jt.pojo.DemoUser"> select * from demo_user where sex= #{sex} and age > #{age} </select> <!-- 如果参数被@Param("sex") String sex修饰 则#{参数key}即可获取数据 --> <select id="findBySA2" resultType="com.jt.pojo.DemoUser"> select * from demo_user where sex= #{sex} and age > #{age} </select> <!-- Map<String, Object> map sex=女 age=18 规则: 如果参数是一个map集合,则通过#{key}获取数据. --> <select id="findBySA3" resultType="com.jt.pojo.DemoUser"> select * from demo_user where sex= #{sex} and age > #{age} </select>
参数知识点总结:
-
如果参数采用对象封装,则可以使用#{属性}取值.
-
如果参数有多个,可以封装为map实现参数的传递. 可以利用#{key}获取数据
-
也可以使用@Param将多个参数封装为map, 利用#{key}的方式获取数据
#号和$符用法
规则说明
-
使用#{} 获取数据时,默认有预编译的效果.防止sql注入攻击.
-
mybatis使用#{}获取数据时,默认为数据添加一对""号.
-
当以字段名称为参数时,一般使用${},但是这样的sql慎用. 可能出现sql注入攻击问题.
注意:一般条件下,能用#{},尽量不用${}
案例实现
编写测试方法
/** *需求:按照指定的age排序 * sql:select * from demo_user order by age * #号 与 $符用法: * */ @Test public void testFindOrder(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); String csc = "age"; List<DemoUser> list = demoUserMapper.findFO(csc); System.out.println(list); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { List<DemoUser> findFO(String csc); }
编写xml文件
<select id="findFO" resultType="com.jt.pojo.DemoUser"> select * from demo_user order by ${csc} </select>
Mybatis 常规CURD操作
新增、删除、更改操作
编写测试方法
/** * 需求:实现用户入库的操作 */ @Test public void saveUser(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(null,"常世超",18,"女" ); int rows= demoUserMapper.saveUser(demoUser); if(rows>0){ System.out.println("影响的行数"+rows); sqlSession.commit(); } sqlSession.close(); } /** * 作业: * 1.把id=1的数据 name 改为 "守山大使" age=5000 * 2.将name="常世超"的数据 删除 */ @Test public void updateUser(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(1, "守山大使", 5000, ""); int rows = demoUserMapper.updateUser(demoUser); if(rows>0){ System.out.println("影响行数"+rows); sqlSession.commit(); } sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); String name = "常世超"; demoUserMapper.deleteUser(name); System.out.println("删除成功"); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { int saveUser(DemoUser demoUser); int updateUser(DemoUser demoUser); void deleteUser(String name); }
编写xml文件
<insert id="saveUser"> insert into demo_user value (null ,#{name} ,#{age},#{sex}) </insert> <update id="updateUser"> update demo_user set name = #{name} ,age = #{age} where id =#{id} </update> <delete id="deleteUser"> delete from demo_user where name =#{name} </delete>
总结
-
编写删除、更改、新增操作时,需要提交事务---》mybatis会自动回滚事务
-
提交事务的两种方式:
-
SqlSession sqlSession = sqlSessionFactory.openSession(true);
-
sqlSession.commit();
-
Mybatis中的转义标签
xml转义语法
xml文件中的转义字符: > > 大于 < < 小于 & & 号 说明:如果sql中有大量的转义字符 建议使用转义标签体 语法: <![CDATA[ xxx内容 报文 ]]>
编写测试方法
/** * 需求: 查询age> 18 and age< 100 的用户信息. * 规则: 如果不能使用对象封装,则一般使用Map集合 */ @Test public void testSelect01(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); Map<String,Object> map = new HashMap<>(); map.put("minAge",18); map.put("maxAge",100); List<DemoUser> userList = demoUserMapper.findByAge(map); System.out.println(userList); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { List<DemoUser> findByAge(HashMap<String, Object> map); }
编写xml文件
<select id="findByAge" resultType="com.jt.pojo.DemoUser"> <!-- select * from demo_user where age >#{minAge} and age < #{maxAge}--> <![CDATA[ select * from demo_user where age >#{minAge} and age < #{maxAge} ]]> </select>
Mybatis集合用法--批量
需求分析
-
要求批量的删除数据库中的记录.
-
规则: 如果遇到相同的多个数据,则一般采用集合的方式封装数据
封装方式
-
array
-
list
-
map<list>
编写测试方法
//1.array封装 @Test public void deleteIds(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); int[] ids = {231,232,233}; demoUserMapper.deleteIds(ids); System.out.println("删除操作成功"); sqlSession.close(); } //2.list封装 @Test public void deleteList(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); List list = new ArrayList(); list.add(231); list.add(232); list.add(233); demoUserMapper.deleteList(list); System.out.println("删除操作成功"); sqlSession.close(); } //3.map封装 @Test public void deleteMap(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); List list = new ArrayList(); list.add(231); list.add(232); list.add(233); Map map = new HashMap(); map.put("ids", list); demoUserMapper.deleteMap(map); System.out.println("删除操作成功"); sqlSession.close(); }
编写业务接口
public interface DemoUserMapper { void deleteIds(int[] ids); void deleteList(List list); void deleteMap(Map map); }
编写xml文件
<delete id="deleteIds"> delete from demo_user where id in <foreach collection="array" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete> <delete id="deleteList"> delete from demo_user where id in <foreach collection="list" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete> <delete id="deleteMap"> delete from demo_user where id in <foreach collection="ids" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>
总结:
批量删除数据 难点:如果使用#{集合}获取的是集合的对象,删除无效 思路:将数组拆分为单个数据===》通过遍历数组的方式获取 语法:mybatis中为了遍历方便,提供了遍历的标签 ===》foreach 关于标签参数的说明: 1.collection 1).如果参数传参为数组, 则collection="array" 2).如果参数传参为list集合, 则collection="list" 3).如果参数传参为map集合, 则collection="map集合中的key值" 关于标签属性的说明: 1.collection 集合的名称 2.item 每次遍历的数据的性参变量 3.open 循环开始的标签 4.close 循环结束的标签 5.separator 循环遍历的分割符 6.index 循环遍历下标
模糊查询说明
需求: 查询name中包含"精"的数据.并且按照年龄降序排列
-
在传参为:String = "%精%"
-
在SQL语句中:select * from demo_user where name like "%"#{name}"%"
Mybatis的优化设置
Mybatis的核心配置文件是有顺序的
顺序为:
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
Mybatis的简化---别名
-
别名标签
-
别名包
-
使用注解定义别名
别名标签
位置:核心配置文件中
说明:只对某个类生效
<typeAliases> <typeAlias type="com.jt.pojo.DemoUser" alias="DemoUser"></typeAlias> </typeAliases> <!-- type: pojo对象全路径 alias: 别名 -->
别名包
位置:核心配置文件中
说明:对包下的所有对象类生效
<typeAliases> <package name="com.jt.pojo"/> </typeAliases>
使用注解
位置:POJO类上
说明:只对某个类生效
@Alias("DemoUser")--->起别名 public class DemoUser implements Serializable{//5.实现序列化 private Integer id; private String name; private Integer age; private String sex; }
Mybatis的简化---SQL标签
说明
mybatis的xml映射文件中会有大量的Sql语句. 随着业务的增加,Sql语句的数量也会增加. 其中有部分"Sql片段"则可能重复. 如果想简化Sql语句,则可以使用Sql标签简化操作.
例子:
select id,name,age,sex from demo_user where id = 1 select id,name,age,sex from demo_user where name = xxx
sql标签的用法
<!-- 提取公共的sql--> <sql id="demo_user_sql"> select id,name,age,sex from demo_user </sql> <!-- include : 代表包含sql标签 refid : 引用sql标签ID --> <select id="findAll" resultType="DemoUser"> <include refid="demo_user_sql"/> </select>
sql标签的优点和缺点
优点:
-
使用Sql标签可以节省xml的文件大小.
-
代码的结构相对简单.
缺点:
-
Sql只能抽取公共的Sql语句,局限性稍大.
-
如果大量的使用Sql标签,则代码的可读性差
Mybatis优化之开启驼峰映射规则
说明:
在mybatis映射数据时,经常出现字段名称与属性名称不一致的现象. 但是其中一部分可以采用驼峰规则的方式完成自动映射. 所以有如下的配置
编写xml核心配置文件
位置:核心配置文件中configuration配置标签之下
<!--Mybatis的核心配置--> <settings> <!--开启了驼峰映射规则 dept_id 自动映射到 deptId --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
Mybatis之动态sql
IF-WHERE用法
需求:根据user对象查询数据.要求根据对象中不为null的属性充当where条件.实现动态的查询
解释:
用户可以根据用户名查询数据、也可以根据用户名+身份证号查询数据、也可以跟据3个条件查询
编辑测试方法
/** * 封装DemoUser的对象,根据对象中不为null的属性查询 */ @Test public void testWhere(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(); demoUser.setName("金角大王") .setAge(3000) .setSex("男"); List<DemoUser> list = demoUserMapper.findWhere(demoUser); System.out.println(list); sqlSession.close(); }
编辑接口
public interface DemoUserMapper { List<DemoUser> findWhere(DemoUser demoUser); }
编辑xml映射文件
<!--动态Sql案例 思路: 如果数据不为null,mybatis才会当做条件 if标签说明: test: 判断的条件 直接写属性即可 where标签: 去除条件中多余的 and 或者 or的 说明: if和 where 几乎一起出现. --> <select id="findWhere" resultType="DemoUser"> select id,name,age,sex 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>
动态sql----SET标签
说明:根据对象不为null的属性当做set条件
编辑测试方法
/** * 根据ID,动态的实现数据的更新 */ @Test public void testUpdateSet(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(); demoUser.setName("黑黑") .setId(1) .setSex("女") .setAge(10000); demoUserMapper.findUpdateSet(demoUser); System.out.println("修改成功"); sqlSession.close(); }
编辑接口
public interface DemoUserMapper { void findUpdateSet(DemoUser demoUser); }
编辑xml文件
<!-- 规则:根据对象不为null的属性当做set条件 set标签说明: 去除set条件中多余的 , 号 --> <update id="findUpdateSet"> 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>
动态Sql-choose when otherwise
需求:
根据条件实现数据的查询. 如果存在name则按照name查询,否则按照sex查询. 补充说明: 条件: name = “张三” , sex=“男” select * from demo_user where name = #{name}
编写测试类
/** * 需求:如果存在name则按照name查询,否则按照sex查询 */ @Test public void testSelectChoose(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DemoUserMapper demoUserMapper = sqlSession.getMapper(DemoUserMapper.class); DemoUser demoUser = new DemoUser(); demoUser.setName("白骨精").setSex("女"); List<DemoUser> list = demoUserMapper.findChoose(demoUser); System.out.println(list); sqlSession.close(); }
编写接口
public interface DemoUserMapper { List<DemoUser> findChoose(DemoUser demoUser); }
编写xml映射文件
<!-- 需求:如果不想将全部的条件当做if的判断,则mybatis提供了分支结构 语法说明: choose:代表分支结构,只有一个条件生效 when:指定判断的条件,和if类似 otherwise:如果上述的条件都不满足时,该行代码有效 --> <select id="findChoose" resultType="DemoUser"> select * from demo_user <where> <choose> <when test="name != null">name = #{name}</when> <otherwise>sex = #{sex}</otherwise> </choose> </where> </select>
resultType和resultMap用法
标签说明
resultType说明: 当结果集中的字段名称,如果与属性的名称一致时,才会实现自动的数据封装
resultMap说明: 当结果集中的字段名称,与对象中的属性不一致时,可以使用resultMap实现自定义的封装
编辑测试类
@Test public void testFindDept(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class); List<Dept> list = deptMapper.findAll(); System.out.println(list); sqlSession.close(); }
编辑接口
public interface DemoUserMapper { List<Dept> findAll(); }
编辑xml映射文件
<!-- ORM思想:对象关系映射 字段:dept_id dept_name 属性:deptId deptName resultType说明: 当结果集中的字段名称,如果与属性的名称一致时,才会实现自动数据封装 resultMap说明: 当结果集中的字段名称,如果与属性的名称不一致时,可以自定义封装 --> <select id="findAll" resultMap="DeptRM"> select * from dept </select> <!-- 自定义映射关系 语法: 1.id标签:代表主键配置信息 1.)column:结果集中的字段 2.)property:对象中的属性 2.result标签:除了主键之外的配置信息 --> <resultMap id="DeptRM" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"/> </resultMap>
resultType的返回类型
对象类型
-
接口的返回值是对象
-
接口返回值是list集合,mybatis会把student对象放入list集合中
-
resultType 指定查询数据封装到那个对象中
定义接口
Student selectById(Integer id );
编辑xml映射文件
<select id = "selectById" resultType="Student"> select id,name,age from student where id = #{id} </select> <!-- resultType使用的java类型的全限定名称,或者别名。 表示的意思是:mybatis执行sql,把ResultSet中的数据转为Student类型的对象 Mybatis的操作: 1. 调用Student的无参构造函数,创建对象 Student student = new Student();//使用反射创建对象 2. 同名的列赋值给同名的属性 student.setId(ResultSet.getInt("id")) student.setName(ResultSet.getString("name")) 3. 得到java对象 -->
简单类型
-
xml文件中执行sql语句,得到的一个值(一行一列)
-
resultType 需要指定java.long包下的数据类型
定义接口
public interface DemoUserMapper { long countStudent(); }
编辑xml映射文件
<select id = "countStudent" resultType="java.lang.Long"> select count(*) from student </select>
Map类型
-
执行sql语句得到的是一个Map结构数据,Mybatis执行sql,把ResultSet转为map
sql执行结果, 列名做map的key,列值做value
sql执行得到的是一行记录,转为map结构是正确的
dao接口返回是一个map,sql语句最多能获取一行记录,多余一行是错误的
-
resultType 需要指定java.util.HashMap
定义接口
//查询结构返回是一个map public interface DemoUserMapper { Map<Object,Object> selectMap(Integer id); }
编辑xml映射文件
<select id = "countStudent" resultType="java.util.HashMap"> select id,name from student where id = #{id} </select>
关联关系
常见的关联关系
-
一对一
-
一对多
-
多对多
一对一
规则:使用对象封装
关联查询实现一对一查询
编写测试类
/** * 完成一对一映射 * 规定:一个员工对应一个部门 * 选取方向:员工方 * 需求:需要在员工中,完成部门的封装 */ @Test public void testOneToOne(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); List<Emp> list = empMapper.findAll(); System.out.println(list); sqlSession.close(); }
编写接口
public interface EmpMapper { List<Emp> findAll(); }
编写xml映射文件
<select id="findAll" resultMap="empRM"> select e.id,e.name,e.age,d.dept_id,d.dept_name from emp e,dept d where e.dept_id = d.dept_id </select> <!-- 规则: 1.如果映射的字段与对象的属性一致,则可以省略不写。 2.最好保留主键的信息 3.如果需要封装单个对象,则使用association标签 3.1.property代表对象的属性(名) 3.2.javaType指定属性的类型,注意路径 4.如果遇到关联封装,必须全部配置映射关系。 如果属性与字段名称一致,可以使用autoMapping="true"来实现自动映射 --> <resultMap id="empRM" type="Emp" autoMapping="true"> <id column="id" property="id"/> <!-- 完成dept对象的封装--> <association property="dept" javaType="Dept"> <id column="dept_id" property="deptId"/> <result column="dept_name" property="deptName"/> </association> </resultMap>
子查询实现一对一查询
编写测试类
@Test public void testOneToOne2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); List<Emp> list = empMapper.findAllWhere(); System.out.println(list); sqlSession.close(); }
编写接口
List<Emp> findAllWhere();
编写xml映射文件
<!-- 子查询的说明: 1.column="子查询的字段信息" 2.select= "sql的ID" 作用:根据column中的数据 实现子查询!!! --> <resultMap id="empRM2" type="emp" autoMapping="true"> <id column="id" property="id"/> <association property="dept" javaType="Dept" column="dept_id" select="findDept"/> </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"/> <result column="dept_name" property="deptName"/> </resultMap>
关联查询与子查询的区别
<!-- 关联查询:通过大量的sql语句,实现数据的关联查询。其中sql语句复杂,封装简单 子查询:通过子查询的方式实现复杂的数据封装。其中sql几乎都是单表查询,sql简单。但是数据封装复杂 -->
一对多
编写测试文件
@Test public void testOneToMore(){ SqlSession sqlSession = sqlSessionFactory.openSession(); DeptMapper deptMapper= sqlSession.getMapper(DeptMapper.class); List<Dept> list = deptMapper.findDept(); System.out.println(list); sqlSession.close(); }
编写接口
List<Dept> findDept();
编写xml文件
<!-- 1.关于一对多 数据封装说明: collection:封装集合的固定写法 property:指定属性 ofType: 封装list集合的泛型对象 2.如果开启驼峰映射规则,可以简化赋值过程 3.autoMapping="true" 自动实现映射 --> <select id="findDept" resultMap="CSC"> 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="CSC" type="Dept" autoMapping="true"> <id column="dept_id" property="deptId"/> <!-- <id column="dept_name" property="deptName"/>--> <collection property="emps" ofType="Emp" autoMapping="true"> <id column="id" property="id"/> </collection> </resultMap>
Mybatis的缓存机制
什么是缓存机制
-
引入缓存可以有效降低用户访问物理设备的频次.提高用户响应速度.
-
扩展: 1.mybatis自身缓存 一级缓存/二级缓存 2.Redis缓存 读取10万次/秒, 写 8.6万次/秒
一级缓存
概念说明: Mybatis默认开启一级缓存, 一级缓存可以在同一个SqlSession对象中查询相同的数据,可以实现数据的共享(缓存操作).
缓存测试
/** * Mybatis一级缓存:默认开启 * 规则:同一个SqlSession内部有效 * 不同的SqlSession内部无效 * 测试: * 看sql执行几次 * 执行一次有缓存 */ @Test public void cache1(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); List<Emp> list1 = empMapper.findAll(); List<Emp> list2 = empMapper.findAll(); List<Emp> list3 = empMapper.findAll(); System.out.println(list1==list2); sqlSession.close(); }
二级缓存
说明: 二级缓存mybatis中默认也是开启的.但是需要手动标识. 二级缓存可以在同一个SqlSessionFactory内部有效.
注:要启动全局的二级缓存,需要在其SQL映射文件中添加
<cache/>
缓存测试
/** * 二级缓存说明: * SqlSession查询数据之后,会将缓存信息保存到一级缓存中,但是不会立即将数据交给二级缓存保存 * 如果需要使用二级缓存,则必须将SqlSession业务逻辑执行成功之后,再关闭 */ @Test public void cache2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); empMapper.findAll(); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpMapper empMapper2 = sqlSession2.getMapper(EmpMapper.class); empMapper2.findAll(); sqlSession2.close(); }