MyBatis
代理模式
-
代理模式的作用:
-
控制目标对象的访问
-
增强功能
-
静态代理
-
静态代理的特点
-
目标对象和代理对象实现同一个业务接口
-
目标对象必须实现接口
-
代理对象再程序运行前就已经存在
-
能够灵活的进行目标对象的切换,却无法进行功能的灵活处理(动态代理的主要解决此问题)
-
-
静态代理的实现:静态代理要求目标对象和代理对象实现同一个业务接口,代理对象中的核心功能是由目标对象来完成,代理对象负责增强功能
-
目标对象:实现业务接口中的功能
-
代理对象:完成除主业务之外的其他业务(实现业务接口,但业务功能必须由目标对象亲自实现)
-
在测试时,有接口和实现类,必须使用接口指向实现类(规范)
面向接口编程
-
类中的成员变量设计为接口,方法的形参设计为接口,方法的返回值设计为接口,调用时接口指向实现类
动态代理
-
代理对象在程序中运行的过程中动态在内存中构建,可以灵活的进行业务功能的切换
JDK动态代理
-
JDK动态代理的特点
-
目标对象必须实现业务接口
-
代理对象不需要实现业务接口
-
动态代理的对象在程序运行前不存在,在程序运行时动态的在内存中构建
-
动态代理灵活地进行业务功能的切换
-
本类中的方法(非接口中的方法)是不能被代理的,并不能将Proxy类型的数据转换成实现类的数据(会报出异常,类转换异常ClassCastException)
-
-
JDK动态代理用到的类和接口
-
是使用现有的工具类完成JDK的动态实现
-
Proxy类
-
是java.lang.reflect.Proxy包下的类,使用Proxy.newProxyInstance()方法专门用来生成动态代理对象
-
public static Object newProxyInstance(ClassLoader loader,//类加载器 Class<?>[] interfaces,//目标对象实现的所有接口 InvocationHandler h//类似于Agent代理对象的功能,代理的功能和目标对象的业务功能调用在这 ) throws IllegalArgumentException {...}
-
-
Method类
-
反射使用的类,用来进行目标对象的方法的反射调用
-
Method对象接受我们正在调用的方法,sing(),show()
-
method.invoke();-->手工调用目标方法
-
-
InvocationHandler接口
-
实现代理和业务功能的,调用时使用匿名内部实现
-
-
-
在代码中进行动态代理的实现
-
public class ProxyFactory { //类中的成员变量设计为接口,目标对象 Service service; //传入目标对象 public ProxyFactory(Service service) { this.service = service; } //返回代理对象 public Object getService(){ return Proxy.newProxyInstance( //ClassLoader loader,类加载器,完成目标对象的加载 service.getClass().getClassLoader(), //Class<?>[] interfaces,目标对象实现所有接口 service.getClass().getInterfaces(), //InvocationHandler h);实现代理功能的接口,我们使用的是匿名内部实现 new InvocationHandler() { @Override public Object invoke( //创建代理对象 Object proxy, //method就是目标方法sing(),show() Method method, //目标方法的参数 Object[] args) throws Throwable { //代理功能 System.out.println("预定时间..."); //代理功能 System.out.println("预定场地..."); //主业务功能 //无论传入的什么方法,都会进行调用 Object invoke = method.invoke(service, args); //代理功能 System.out.println("结算费用..."); //目标方法的返回值 return invoke; } } ); } }
-
CGLib动态代理(子类代理)
-
通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强,若目标对象没有实现接口,则只能通过CGLib子类代理来进行功能的增强
-
子类代理是通过对象字节码框架ASM来实现的
-
后期可以通过spring框架来轻松实现
-
工具类Enhancer en = new Enhancer()完成子类代理对象的创建
-
en.setSuperclass(service.getClass())设置父类
-
en.setCallback(this(这里指的是成员变量));设置回调函数
-
en.create();创建子类对象
-
注意:
-
被代理的类不能被final修饰
-
目标对象的方法如果为final/static,那么就不会拦截,即不会执行目标对象额外的业务方法
-
MyBatis框架
框架概述
三层架构
-
在项目的开发中,遵循的一种形式模式:分为三层
-
界面层:用来接受客户端的输入,调用业务逻辑层进行功能处理,返回结果给客户端,过去的servlet就是界面层的功能
-
业务逻辑层:用来进行整个项目的业务逻辑处理,向上为界面层提供处理结果,向下对数据访问层索要数据
-
数据访问层:专门用来进行数据库的增删改查操作,向上为业务逻辑层提供数据
-
各层之间的调用顺序是固定的,不允许跨层访问
-
界面层-------->业务逻辑层------>数据访问层
-
界面层<-------业务逻辑层<------数据访问层
MyBatis框架入门
添加框架的步骤:
-
添加依赖
-
添加配置文件
具体步骤:
-
创建数据库,新建表
-
新建maven项目,选quickstart模板
-
修改目录,添加缺失的目录,修改目录属性
-
修改pom.xml文件,添加MyBatis的依赖,添加mysql依赖
-
<!--mybatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!--mysql依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency>
-
-
修改pom.xml文件,添加资源文件指定
-
<!--添加资源文件的指定--> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources> </build>
-
-
在idea中添加数据库的可视化
-
添加jdbc.properties属性文件(数据库的配置)
-
添加SqlMapConfig.xml文件(系统核心配置文件),MyBatis的核心配置文件
-
头文档文件位置
-
<?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> <!-- 读取属性文件 属性: resource:从resource目录下寻找指定名称的properties文件 url:使用绝对路径寻找加载文件 --> <properties resource="jdbc.properties"></properties> <!-- SqlMapConfig.xml文件配置 配置数据库环境变量(数据库连接配置) default:在environments标签中对指定id的environment进行配置 --> <environments default="development"> <!-- 测试环境变量--> <!-- environment中的id就是为了给environments中的default使用--> <environment id="development"> <!-- 配置事务管理器 type:是为了指定事务管理的方式 JDBC:事务的管理通过程序员来完成 MANAGED:事务的管理通过框架(spring)来完成 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源 type:指定不同的配置方式 JNDI:java命令目录接口,在服务端进行数据库连接池的管理 POOLED:使用数据库连接池 UNPOOLED:不使用数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> <!-- 家庭环境变量--> <!-- <environment id="home">--> <!-- <transactionManager type=""></transactionManager>--> <!-- <dataSource type=""></dataSource>--> <!-- </environment>--> <!--<!– 上线环境变量–>--> <!-- <environment id="online">--> <!-- <transactionManager type=""></transactionManager>--> <!-- <dataSource type=""></dataSource>--> <!-- </environment>--> </environments> <!--注册mapper.xml文件 resource:从resource目录下寻找指定名称的mapper.xml文件 url:使用绝对路径寻找mapper.xml文件 class:动态代理方式下的注册 --> <mappers> <mapper resource="StudentMapper.xml"></mapper> </mappers> </configuration>
-
-
创建实体类Student,用来封装数据
-
添加完成学生表的增删改查的功能的StudentMapper.xml文件
-
<?xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--mapper标签是整个xml的结束和开始--> <!--当完成mapper的编写之后需要在SqlMapConfig中对mapper进行注册--> <!--namespace:指定命名空间(相当于包名),用来区分不同mapper.xml文件中相同的id属性--> <mapper namespace="zar"> <!-- 完成查询所有学生的功能 resultType:若没有参数,则指定返回数据的集的类型,如果是集合,则必须是泛型的类型 parameterType:若有参数,则通过他来指定参数的类型 --> <select id="getAll" resultType="com.wsh.bean.Student"> select id,name,email,age from student </select> <!-- 完成根据id主键对学生的查询 getById(Integer id); --> <select id="getById" parameterType="int" resultType="com.wsh.bean.Student"> select id,name,email,age from student where id = #{id} </select> <!-- 按学生名称模糊查询 List<Student> getByName(String name) --> <select id="getByName" parameterType="string" resultType="com.wsh.bean.Student"> select id,name,email,age from student where name like '%${name}%' </select> <!-- 增加学生 int insert(Student stu) 实体类属性: private Integer id; private String name; private String email; private Integer age; 占位符上可以使用实体类属性名称使用 --> <insert id="insert" parameterType="com.wsh.bean.Student"> insert into student (name,email,age) values (#{name},#{email},#{age}) </insert> <!-- 根据主键来删除 int delete (Integer id) --> <delete id="delete" parameterType="int"> delete from student where id = #{id}; </delete> <!-- 根据主键修改更新 int update(Student stu) 实体类属性: private Integer id; private String name; private String email; private Integer age; 占位符上可以使用实体类属性名称使用 --> <update id="update" parameterType="com.wsh.bean.Student"> update student set name = #{name},email = #{email},age = #{age} where id = #{id} </update> </mapper>
-
-
创建测试类进行测试
-
public class Test { @org.junit.Test public void testGetAll() throws IOException { //使用文件流读取核心配置文件SqlMapConfig.xml文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //创建SqlSessionFactory工厂 SqlSessionFactory factoryBuilder = new SqlSessionFactoryBuilder().build(in); //取出sqlSession对象 SqlSession sqlSession = factoryBuilder.openSession(); //完成查询操作 List<Student> students = sqlSession.selectList("zar.getAll"); students.forEach(student -> System.out.println(student)); //关闭sqlSession sqlSession.close(); } @org.junit.Test public void testGetById() throws IOException { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); Student student = sqlSession.selectOne("zar.getById",1); System.out.println(student); sqlSession.close(); } @org.junit.Test public void testGetByName() throws IOException { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); List<Student> students = sqlSession.selectList("zar.getByName","张三"); students.forEach(student -> System.out.println(student)); sqlSession.close(); } @org.junit.Test public void testInsert()throws IOException{ InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); Student student = new Student(); student.setName("赵六"); student.setId(6); student.setEmail("zhaoliu@qq.com"); student.setAge(34); sqlSession.insert("zar.insert",student); //在数据库的增删改之后必须手动的提交事务 sqlSession.commit(); sqlSession.close(); } @org.junit.Test public void testDelete() throws IOException { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); sqlSession.delete("zar.delete",2); sqlSession.commit(); sqlSession.close(); } @org.junit.Test public void testUpdate() throws IOException { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = factory.openSession(); sqlSession.update("zar.update",new Student(3,"万三","wansan@126.com",23)); sqlSession.commit(); sqlSession.close(); } }
-
MyBatis对象分析
-
Resource类
-
就是解析SqlMapConfig.xml文件,创建出相应的对象
-
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
-
-
SqlSessionFactory接口
-
使用快捷键Ctrl+H查看本接口的子接口及其实现类
-
DefaultSqlSessionFactory是本接口的实现类
-
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
-
-
SqlSession接口
-
DefaultSqlSession是本接口的实现类
-
SqlSession sqlSession = factory.openSession();
-
简化Test测试类
-
public class Test { SqlSession sqlSession; @Before public void openSqlSession() throws IOException { //使用文件流读取核心配置文件SqlMapConfig.xml文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //创建SqlSessionFactory工厂 SqlSessionFactory factoryBuilder = new SqlSessionFactoryBuilder().build(in); //取出sqlSession对象 sqlSession = factoryBuilder.openSession(); } @After public void closeSqlSession(){ //关闭sqlSession sqlSession.close(); } @org.junit.Test public void testGetAll() throws IOException { //完成查询操作 List<Student> students = sqlSession.selectList("zar.getAll"); students.forEach(student -> System.out.println(student)); } @org.junit.Test public void testGetById(){ Student student = sqlSession.selectOne("zar.getById",1); System.out.println(student); } @org.junit.Test public void testGetByName(){ List<Student> students = sqlSession.selectList("zar.getByName","张三"); students.forEach(student -> System.out.println(student)); } @org.junit.Test public void testInsert(){ Student student = new Student(); student.setName("赵六"); student.setId(6); student.setEmail("zhaoliu@qq.com"); student.setAge(34); sqlSession.insert("zar.insert",student); //在数据库的增删改之后必须手动的提交事务 sqlSession.commit(); } @org.junit.Test public void testDelete(){ sqlSession.delete("zar.delete",2); sqlSession.commit(); } @org.junit.Test public void testUpdate(){ sqlSession.update("zar.update",new Student(3,"万三","wansan@126.com",23)); sqlSession.commit(); } }
关于对Mapper.xml文件的优化
-
首先在SqlMapConfig.xml中进行typeAlias的配置
-
<!-- 完成实体类的别名注册--> <typeAliases> <!-- 单个实体类的别名注册--> <!-- <typeAlias type="com.wsh.bean.Student" alias="student"></typeAlias>--> <!-- 批量的对实体类进行注册 别名是类名的驼峰命名(规范) --> <package name="com.wsh.bean"/> </typeAliases>
-
配置完成之后,Mapper.xml文件中的parameterType="com.wsh.bean.Student"-------->parameterType="student"(类名进行驼峰命名后的名字),同理resultType也是同样的处理方式
-
parameterType="student" resultType="student"
设置日志的输出
-
<settings> <!--固定格式--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
MyBatis框架动态代理
-
在三层架构中,业务逻辑层要通过接口访问数据访问层的功能,则需要动态代理来实现
Mapper.xml文件的优化
-
<mappers> <!-- 单个注册--> <!-- <mapper class="com.wsh.mapper.UsersMapper"></mapper>--> <!-- 批量注册--> <package name="com.wsh.mapper"/> </mappers>
#{}和${}的区别
-
#{}是占位符
-
传参一般都会使用#{},他的底层使用的是PreparedStatement对象,是安全的数据库访问,防止sql注入
-
#{}内容如何写,看parameterType参数的类型,两种情况
-
如果parameterType的类型是简单的类型)(八种基本类型(封装)+String),则#{}可以写任意的内容
-
若parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写
-
-
-
${}字符串拼接或字符串替换
-
字符串拼接一般用于模糊查询中,建议少用,因为有sql注入的风险
-
${}内容的书写也有两种情况:
-
若parameterType的类型是简单类型,则在3.5.1及其以下版本只写value
-
若parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称
-
-
-
字符串替换
-
//模糊查询用户名和地址,接口中编写的内容 List<User> getByNameOrAddress(@Param("columnName") String columnName,@Param("columnValue") String columnValue);
-
<!-- //模糊查询用户名和地址 //当遇到两个及两个以上的参数则不需要写parameterType //在sql语句中可以使用注解的名称 List<User> getByNameOrAddress( @Param是为了在sql语句中使用该名称 @Param("columnName") String columnName, @Param("columnValue") String columnValue); --> <select id="getByNameOrAddress" resultType="user"> select id,username,brithday,sex,address from users where ${columnName} like concat('%',#{columnValue},'%') </select>
-
-
关于模糊查询的优化
-
使用#{}来防止sql的注入
-
<select id="getByName" parameterType="string" resultType="user"> select id,username,brithday,sex,address from users where username like concat('%',#{name},'%') </select>
-
注意:
-
在使用concat()方法时字符串的拼接需要使用","而不是"+"。
-
返回主键的业务需求
-
<selectKey keyProperty="id" resultType="int" order="AFTER"> select last_insert_id() </selectKey>
-
在插入语句结束之后,使用该方法可以返回加入用户的自增的主键(id)值
-
<selectKey>参数的含义:
-
keyProperty:user对象使用那个属性来接取返回的主键值
-
resultType:返回的主键值的类型
-
order:该方法是在插入语句之前执行还是在插入语句之后执行
-
UUID的使用
-
是一个全球唯一的字符串,由36个字母和中划线组成
-
//使用UUID创建一个随机的UUID UUID uuid = UUID.randomUUID(); //2cc701c4-4253-4e69-b9f7-e165ef5c48d5 //d2f82bec7f5c System.out.println(uuid.toString().replaceAll("-","").substring(20));
-
可以使用对字符串的操作在控制UUID的输出
动态sql
什么是动态sql
-
可以定义代码片段,可以进行逻辑的判断,可以进行循环处理(批量处理),使得条件判断更加简单
动态sql的标签
-
<sql>:用来定义代码片段,可以将所有的列名,或复杂的条件定义为代码片段,供使用时调用
-
<include>:用来引用<sql>定义的代码片段
-
例子:
-
<!-- 自定义代码片段--> <sql id="allColumns"> id,username,brithday,sex,address </sql> <select id="getAll" resultType="user"> <!--对于sql标签定义的代码片段进行引用--> select <include refid="allColumns"></include> from users </select>
-
-
<if>:进行条件的判断
-
<where>:进行多条件的拼接,在查询,删除,更新中使用
-
例子:
-
<!-- //按指定的条件进行多条件的查询 List<User> getByCondition(User user); 条件就是判断实体类中的成员变量是否有值来决定是否添加条件 --> <select id="getByCondition" parameterType="user" resultType="user"> select <include refid="allColumns"></include> from users <where> <if test="userName != null and userName != ''"> and username like concat('%',#{userName},'%') </if> <if test="birthday != null"> and birthday = #{birthday} </if> <if test="sex != null and sex != ''"> and sex = #{sex} </if> <if test="address != null and address != ''"> and address like concat('%',#{address},'%') </if> </where> </select>
@org.junit.Test public void testGetByCondition() throws ParseException { // //按指定的条件进行多条件的查询 // List<User> getByCondition(User user); User user = new User(); user.setSex("1"); user.setBrithday(sf.parse("2001-01-01")); List<User> users = usersMapper.getByCondition(user); users.forEach(user1 -> System.out.println(user1)); }
-
-
<set>:有选择的进行更新,至少更新一列
-
例子:
-
<!-- //有选择的进行更新 int updateBySet(User user); --> <update id="updateBySet" parameterType="user"> update users <set> <if test="userName != null and userName!=''"> username = #{userName}, </if> <if test="brithday!=null"> brithday = #{brithday}, </if> <if test="sex!=null and sex!=''"> sex = #{sex}, </if> <if test="address!=null and address!=''"> address = #{address}, </if> </set> where id = #{id} </update>
/** * //有选择的进行更新 * int updateBySet(User user); */ @org.junit.Test public void testUpdateBySet() throws ParseException { User u = new User(); u.setId(7); u.setUserName("王仕豪"); u.setBrithday(sf.parse("2001-06-22")); u.setSex("1"); u.setAddress("杭州"); int num = usersMapper.updateBySet(u); System.out.println(num); sqlSession.commit(); }
-
-
<foreach>:主要用来对集合和数组进行遍历
-
拥有参数:
-
collection:选择类型(list、array、map)
-
item:命名(临时变量)
-
separator:多个值或语句之间的分隔符
-
open:前括号(循环外)
-
close:后括号(循环外)
-
-
指定参数位置
-
如果入参是多个,可以通过指定参数位置进行传参,是实体类包含不了的条件,实体类只能封装住成员变量的条件,如果某个成员变量要有区间范围内的判断,或者有两个值进行处理,则实体类包不住
-
例子:指定日期范围内的用户
-
<!-- //指定日期范围进行查询 List<User> getByBrithday(Date begin,Date end); --> <select id="getByBrithday" resultType="user"> select <include refid="allColumns"></include> from users where brithday between #{arg0} and #{arg1} </select>
@org.junit.Test public void testGetByBrithday() throws ParseException { Date begin = sf.parse("1999-02-22"); Date end = sf.parse("2002-01-01"); List<User> users = usersMapper.getByBrithday(begin,end); users.forEach(user -> System.out.println(user)); }
-
入参是Map(重点)
-
当入参超过一个以上,推荐使用Map封装查询条件,更有语义,更加明确
-
例子:
-
<!-- //使用Map对查询条件进行封装 List<User> getByMap(); #{brithdayBegin}就相当于map中的key --> <select id="getByMap" resultType="user"> select <include refid="allColumns"></include> from users where brithday between #{brithdayBegin} and #{brithdayEnd} </select>
@org.junit.Test public void testGetByMap() throws ParseException { Date brithdayBegin = sf.parse("1999-02-22"); Date brithdayEnd = sf.parse("2002-01-01"); Map map = new HashMap(); map.put("brithdayBegin",brithdayBegin); map.put("brithdayEnd",brithdayEnd); List<User> users = usersMapper.getByMap(map); users.forEach(user -> System.out.println(user)); }
-
-
返回值是map
-
如果返回的数据实体类无法包含,可以使用map返回多张表中的若干数据,返回后这些数据之间没有任何关系,就是Object类型
-
返回的map的key就是列名或是别名
-
-
当数据库列名和实体类名称不一致时,推荐使用resultMap完成手工映射(id是select标签中的resultMap自定义的名称,type是注册的实体类的名称)
-
property指的是实体类中的名称
-
column指的是数据库中的名称
-
-
表的关联关系
关联关系是有方向的
-
一对多关联
-
<!--//根据客户id获取该客户下的所有订单 Customer getById(Integer id); //customer三个属性 private Integer id; private String name; private Integer age; //该客户名下的所有订单 List<Orders> ordersList; --> <resultMap id="customermap" type="customer"> <!-- 主键绑定--> <id property="id" column="cid"></id> <!-- 非主键绑定--> <result property="name" column="name"></result> <result property="age" column="age"></result> <!-- 多出来的额外设定 private Integer id; private Integer orderNumber; private Double orderPrice; --> <collection property="ordersList" ofType="orders"> <id property="id" column="oid"></id> <result property="orderNumber" column="orderNumber"></result> <result property="orderPrice" column="orderPrice"></result> </collection> </resultMap> <select id="getById" parameterType="int" resultMap="customermap"> select c.id cid,name,age,o.id oid,orderNumber,orderPrice,customer_id from customer c left join orders o on c.id = o.customer_id where c.id = #{id} </select>
@org.junit.Test public void testGetById(){ Customer customer = customerMapper.getById(3); System.out.println(customer); }
-
-
多对一关联
-
<mapper namespace="com.wsh.mapper.OrdersMapper"> <!-- //根据主键查订单,并返回客户信息 Orders getById(Integer id); private Integer id; private Integer orderNumber; private Double orderPrice; //关联订单的客户信息 private Customer customer; --> <resultMap id="ordersmap" type="orders"> <id property="id" column="oid"></id> <result property="orderNumber" column="orderNumber"></result> <result property="orderPrice" column="orderPrice"></result> <!-- 对于成员变量中的类进行绑定--> <association property="customer" javaType="customer"> <id property="id" column="cid"></id> <result property="name" column="name"></result> <result property="age" column="age"></result> </association> </resultMap> <select id="getById" parameterType="int" resultMap="ordersmap"> select o.id oid,orderNumber,orderPrice,customer_id,c.id cid,name,age from orders o inner join customer c on o.customer_id = c.id where o.id = #{id} </select> </mapper>
-
@org.junit.Test public void testGetOrdersById(){ Orders orders = ordersMapper.getById(11); System.out.println(orders); }
-
-
多对多关联
-
一对一关联
事务
-
多个操作同时完成,或是同时失败成为事务(保证数据库不是出现垃圾数据)
事务的四个特点
-
一致性
-
持久性
-
原子性
-
隔离性
在MyBatis框架中设置事务
-
<transactionManager type="JDBC"></transactionManager> <!--程序员自己手动提交-->
可以设置为自动提交
-
默认是手动提交(false)
-
sqlSession = factoryBuilder.openSession();
-
sqlSession = factoryBuilder.openSession(true);===>为自动提交,在增删改后不需要commit();
-
MyBatis支持两种事务管理器类型:JDBC和MANAGED,MANAGED是容器管理事务的处理方式(交给容器自动提交)
缓存(面试)
-
MyBatis框架有两层缓存(一级缓存,二级缓存),会默认的开启一级缓存
-
缓存就是为了提高效率
使用缓存后查询的流程
-
查询时,先到缓存中查找,若没有则查询数据库,放入缓存中一份,再返回客户端,下次再进行查询的时候直接从缓存中返回数据,不再访问数据库,如果数据库中发生commit()操作,则会清空全部缓存
-
当数据库发生改变时,会清空缓存,再次进行访问会直接到数据库中进行查询
缓存的作用域
-
一级缓存使用的是SqlSession的作用域,同一个SqlSession共享一级缓存的数据
-
二级缓存使用的是Mapper的作用域,不同的SqlSession只要访问同一个mapper.xml文件,就会共享二级缓存中的数据
二级缓存的开启步骤
ORM
-
ORM就是对象关系映射
-
MyBatis框架是一个十分优秀的持久层框架
-
java语言中以对象的方式操作数据,存到数据库中是以表的形式来存储,对象中的成员变量与表中的列之间的数据互换称为映射,整个这一套操作就是ORM
-
持久化的操作:将对象保存到关系型数据库中,将关系型数据库中的数据读取出来以对象的形式封装。
源码追踪
-
在SqlMapConfig中写的标签都会注册到configuration中
Mapper的注册方式
-
四种,分别为
-
package(优先级最高的)
-
resource(第二)
-
url(第三)
-
class(第四)
-
SqlSession解析
-
alt+7:列出本接口或类中的全部成员变量
-
ctrl+h:列出本接口的实现类,或是本类中的所有子类,改变按钮可以得到父类
-
在之前的步骤中存入的configuration中的数据,都会在SqlSession中取出