目录
@注解简单类型(8种基本数据类型(包括封装类)+String):
代理模式:
目标对象不可访问,通过代理对象增强功能访问。
生活中的代理例子:
房东 ===>目标对象
房屋中介 ===>代理对象
你、我 ===>客户端对象
服务生产厂 ===>目标对象
门店(旗舰店) ===>代理对象
你、我 ===>客户端对象
开发中的代理例子:
运营商(电信,移动,联通) ====>目标对象
第三方公司 ====>代理对象
开发的应用程序需要发送短信的功能 ===>客户端对象
代理模式的作用:
1、控制目标对象的访问。
2、增强功能。
代理模式的分类:
1、静态代理。
2、动态代理,又为JDK动态代理,CGLib动态代理(子类代理)。
什么是静态代理:
它是代理模式的一种,它具备以下特点:
1、目标对象和代理对象实现同一个业务接口。
2、目标对象必须实现接口。
3、代理对象在程序运行前就已经存在。
4、能够灵活的进行目标对象的切换,却无法进行功能的灵活处理(使用动态代理解决此问题)。
静态代理实现:
业务功能:请明星进行节目表演。
明星刘德华:目标对象(无法直接访问)。
刘德华助理:代理对象(我们可以访问,他还可以跟明星对接)。
我们:客户端对象。
代码实现:
/**
* 业务接口,规定唱歌的业务功能
*/
public interface Service {
void sing();
}
/**
* 客户端对象。通过代理对象与目标对象对接。
*/
public class test {
public static void main(String[] args) {
Service agent1 = new Agent(new SuperStarLiu());
// 通过含参的构造函数向Agent类传递目标对象类的实例对象
agent1.sing();
Service agent2 = new Agent(new SuperStarZhou());
agent2.sing();
}
}
/**
* 代理对象:助理,是客户端对象与目标对象的对接桥梁
*/
// 目标对象和代理对象实现同一个业务接口。
public class Agent implements Service{
// 类中成员变量类型设计为接口类型
public Service taget;
// 传入目标对象,形参类型设计为接口类型
public Agent(Service taget){
this.taget=taget;
}
@Override
public void sing() {
System.out.println("预定时间...");
System.out.println("预定场地...");
// SuperStarLiu liu = new SuperStarLiu();
// liu.sing();
// SuperStarZhou zhou = new SuperStarZhou();
// zhou.sing();
// 这样代码就写死了,耦合度很高。
// 面向接口编程,接口指向实现类。
taget.sing();
// tager是目标对象类的一个实例对象,根据不同目标对象在自己类对接口sing()方法的实现,可输出不同的语句
// 过程中只需改动目标对象类的代码,不需要改动该类的具体代码,耦合度降低
System.out.println("结算费用...");
}
}
/**
* 目标对象:周杰伦。实现业务接口中的功能,进行唱歌表演
*/
public class SuperStarZhou implements Service{
@Override
public void sing() {
System.out.println("我是周杰伦,我正在表演唱歌...");
}
}
/**
* 目标对象:刘德华。实现业务接口中的功能,进行唱歌表演
*/
public class SuperStarLiu implements Service{
@Override
public void sing() {
System.out.println("我是刘德华,我正在表演唱歌...");
}
}
能够灵活的进行目标对象的切换(华仔和杰伦之间),却无法进行功能的灵活处理(只能是唱歌)。
解决耦合的普遍方法——面向接口编程(重要):
1、类中的成员变量类型设计为接口类型。
2、方法的形参类型设计为接口类型。
3、方法的返回值类型设计为接口类型。
4、调用时接口指向实现类。
静态代理的弊端:
上述演示的静态代理中,倘如接口中的抽象方法有所增加,那么其
实现类(目标对象类、代理对象类)以及测试类
也必须作出相应的更改,耦合度较高,这时候就需要用到我们的动态代理来解决这个问题。
JDK动态代理:
1、目标对象必须实现业务接口,而JDK代理对象不需要实现业务接口。(静态代理的目标对象和代理对象实现同一个业务接口)
2、JDK动态代理的对象在程序运行前不存在,在程序运行时动态的在内存中构建。(静态代理的代理对象在程序运行前就已经存在)
3、JDK动态代理灵活的进行业务功能的切换
4、jdk动态代理能够代理的只是接口中的方法,实现类(目标对象)本身的方法无法代理。
JDK动态代理用到的类和接口:
它是使用现在的工具类完成JDK动态实现.
(1)Proxy类:
它是java.lang.reflect.Proxy包下的类. 它有一个方法Proxy.newProxyInstance(…..)专门用来生成动态代理对象.
public static Object newProxyInstance(ClassLoader loader, //类加载器
Class<?>[] interfaces, //目标对象实现的所有接口
InvocationHandler h //它就类似于Agent的功能,代理的功能和目标对象的业务功能调用在这)
throws IllegalArgumentException {...}
(2)Method类:
反射用的类,用来进行目标对象的方法的反射调用.
method.invoke(); // 反射调用目标方法
(3)InvocationHandler接口:
它是实现代理和业务功能的,我们在调用时使用匿名内部实现。类似于Agent的功能。
演示案例:
/**
* 业务接口
*/
public interface Service {
void sing();
// 如果接口有新增的方法,那么代理对象的代码不用改,目标对象和测试程序改即可
String show(int age);
}
/**
* 目标对象:刘德华
*/
public class SuperStarLiu implements Service {
@Override
public void sing() {
System.out.println("我是刘德华,我正在表演唱歌........");
}
// 如果接口有新增的方法,那么代理对象的代码不用改,目标对象和测试程序改即可
@Override
public String show(int age) {
System.out.println("刘德华的show........."+age);
return "liu";
}
//此方法不能被代理(不以增强功能)
public void one(){
System.out.println("one............");
}
}
/**
* 目标对象:周杰伦
*/
public class SuperStarZhou implements Service {
@Override
public void sing() {
System.out.println("我是周润发,我正在表演唱歌........");
}
// 如果接口有新增的方法,那么代理对象的代码不用改,目标对象和测试程序改即可
@Override
public String show(int age) {
System.out.println("周杰伦的show........."+age);
return "zhou";
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
*/
public class ProxyFactory {
//类中的成员变量设计为接口,目标对象
Service target;
//传入目标对象
public ProxyFactory(Service target){
this.target = target;
}
//返回动态代理对象
public Object getAgent(){
// 所有的代理对象都可以返回,所以返回值是Object类型
return Proxy.newProxyInstance(
// ClassLoader loader, 类加载器,完成目标对象的加载
target.getClass().getClassLoader(),
// Class<?>[] interfaces,目标对象实现的所有接口
target.getClass().getInterfaces(),
// InvocationHandler h,它就类似于Agent的功能,代理的功能和目标对象的业务功能调用在这,
// 代理对象功能的一个实现 ,实现的方法是传入的是匿名内部类/lambda表达式
new InvocationHandler() { // 中文名:调用处理程序
@Override
public Object invoke(
//创建代理对象
Object proxy, // 这个参数不是给我们调用的
//method就是目标方法sing(),show()
Method method, // 当作已经通过反射机制获得了方法就行
//目标方法的参数
Object[] args) throws Throwable {
//代理功能
System.out.println("预订时间........");
//代理功能
System.out.println("预订场地........");
//主业务功能实现
//target.sing();还是写死了方法的调用, 不成
//sing(),show(),one()
Object obj = method.invoke(target,args); // 反射机制调用target(目标对象)的method方法,实参为args。
// 切记:该行代码的返回值是 method通过反射指向的方法 的返回值,可能是任何类型
//代理功能
System.out.println("结算费用........");
return obj; // 切记:这里返回的是所调用方法的返回值,可能是任意类型
}
}
);
}
}
/**
* 测试类
*/
public class MyTest {
public static void main(String[] args) {
/*
案例一
*/
ProxyFactory factory = new ProxyFactory(new SuperStarZhou());
// 通过构造方法传参到ProxyFactory类中的target属性
// 获得动态代理对象(助理/中间者)
Service agent = (Service) factory.getAgent();
// 相当于静态代理一样,代理对象下都有一个跟目标对象一样的接口方法的实现(sing),所以可直接调用
agent.sing();
/*
案例二
*/
ProxyFactory factory2 = new ProxyFactory(new SuperStarZhou());
Service agent2 = (Service) factory.getAgent();
String showReturn = agent2.show(60);
System.out.println(showReturn);
/*
案例三
*/
ProxyFactory factory3 = new ProxyFactory(new SuperStarLiu());
Service agent3 = (Service) factory3.getAgent();
System.out.println(agent3.getClass());
// 注意,返回的动态代理对象不会是SuperStarLiu的类型,而是class com.sun.proxy.$Proxy0类型
Service service = new SuperStarLiu();
System.out.println(service.getClass());
// class com.bjpowernode.service.impl.SuperStarLiu
/*
案例四,jdk动态代理能够代理的只是接口中的方法,实现类本身的方法无法代理。
*/
ProxyFactory factory4 = new ProxyFactory(new SuperStarLiu());
SuperStarLiu agent4 = (SuperStarLiu) factory4.getAgent();
// agent.one();
// Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.bjpowernode.service.impl.SuperStarLiu
// 类型转换异常,因为经过工厂方法后,代理对象的功能已经被增强(多了预定时间、预定地点、结算费用等方法),类型已经变成了class com.sun.proxy.$Proxy0
// 与SuperStarLiu没有了瓜葛,当然不能强转
// 但可以转为接口类型,因为动态代理对象相当于是接口的一个新的实现。!!!!!!!!!!!!!!!
}
}
可以在测试类中任意调用目标对象类实现接口的任一方法(除接口外的方法不行),JDK动态代理更改测试类比静态代理更改底层类更优。
CGLib动态代理:
又称为子类代理。通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强。如果目标对象没有实现接口,则只能通过CGLib子类代理来进行功能增强。
子类代理是对象字节码框架ASM来实现的。
注意:
(1)需要spring-core-5.2.5.jar依赖即可。引入功能包后,就可以在内存中动态构建子类
(2)
被代理的类不能为
final
, 否则报错。(无法继承,无法构建子类)
(3)
目标对象的方法如果为
final/static,
那么就不会被拦截,即不会执行目标对象额外的业务方法。(静态方法没有多态)
(4) 代码实现结构:
ProxyFactory.java:
public class ProxyFactory implements MethodInterceptor {
//目标对象
private Object target;
//传入目标对象
public ProxyFactory(Object target){
this.target=target;
}
//Cglib采用底层的字节码技术,在子类中采用方法拦截的技术,拦截父类指定方法的调用,并顺势植入代理功能的代码
@Override
public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
//代理对象的功能
System.out.println("预定场地............");
//调用目标对象的方法
Object returnValue=method.invoke(target, arg2);
//代理对象的功能
System.out.println("结帐走人............");
return returnValue;
}
//生成代理对象
public Object getProxyInstance(){
//1.使用工具类
Enhancer en=new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理)对象
return en.create();
}
}
测试类中:
public void testCglibProxy(){
SuperStar proxy=(SuperStar) new ProxyFactory(superStar).getProxyInstance();
proxy.sing();
}
动态代理的好处:
静态代理中,倘如接口中的抽象方法有所增加,那么其
实现类(目标对象类、代理对象类)以及测试类
也必须作出相应的更改,耦合度较高。而对于动态代理来说,即便接口中的抽象方法有所增加,代理对象的代码也不用修改,只需修改目标对象的实现和测试类代码即可,降低了耦合度。
换言之,代理对象不再需要实现接口。(通过反射完成调用)
什么是三层架构:
是在项目开发中遵循的一种开发模式。
(1)界面层:用来接收客户端的输入,调用业务逻辑层进行功能处理,返回结果给客户端。过去的servlet就是界面层的功能。
(2)业务逻辑层:用来进行整个项目的业务逻辑处理,向上为界面层提供处理结果,向下问数据访问层要数据。
(3)数据访问层:专门用来进行数据库的增删改查操作,向上为业务逻辑层提供数据。
各层之间的调用顺序是固定的,不允许跨层访问。界面层<——->业务逻辑层<——>数据访问层
为什么需要使用三层架构?
-
结构清晰、耦合度低, 各层
分工明确
- 可维护性高,可扩展性高
-
有利于标准化
-
开发人员可以只关注整个结构中的其中某一层的功能实现
-
有利于各层逻辑的复用
常用的框架SSM:
Spring:它是整合其它框架的框架,它的核心是IOC和AOP。它由20多个模块构成,在很多领域都提供了很好的解决方案,是一个大佬级别的存在。
SpringMVC:它是Spring家族的一员。专门用来优化控制器(Servlet),提供了极简单数据提交,数据携带,页面跳转等功能。
MyBatis:是持久化层的一个框架。用来进行数据库访问的优化,专注于sql语句,极大的简化了JDBC的访问。
什么是框架:
框架是一个半成品软件。将所有的公共的,重复的功能解决掉,帮助程序快速高效的进行开发。它是可复用,可扩展的。
什么是MyBatis框架:
MyBatis 本是 apache 的一个开源项目iBatis,2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github,最新版本是 MyBatis 3.5.7 ,其发布时间是 2021 年 4月 7日。
数据库连接池:
假如没有连接池,那么我们在使用JDBC程序的时候,就会反复的创建连接,销毁连接,大部分时间都会耗费在连接的操作上面,这样做比较消耗资源,并不能做到连接的反复利用。这样做如果这个程序的用户人数很多,那么对性能的影响就会很大。所以为了优化性能,就可以使用数据库连接池,连接池中都是一些已经连接上数据库的对象,每次需要连接对象时可直接从连接池里面获取连接,使用完了连接直接放回连接池,做到连接的反复使用。
MyBatis框架的结构:
1、mybatis配置:SqlMapConfig.xml。此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。 mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂。
3、由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
添加框架的步骤(所有的框架均是如此):
(1)、添加依赖。
(2)、添加配置文件。
具体步骤:
1、新建库,建表
2、新建maven项目。
3、修改pom.xml文件,添加MyBatis的依赖,添加mysql的依赖
4、修改pom.xml文件,添加资源文件指定
5、在idea中添加数据库的可视化
6、添加jdbc.properties属性文件(数据库的配置)
(具体访问哪个数据库在这里配置)
7、添加SqlMapConfig.xml文件,MyBatis的核心配置文件
8、创建实体类Student,用来封装数据
9、添加完成学生表的增删改查的功能的StudentMapper.xml文件
10、创建测试类,进行功能测试
1、新建数据库,建表。
CREATE DATABASE ssm DEFAULT CHARSET utf8;
use ssm;
CREATE TABLE `student` (
`id` int(11) AUTO_INCREMENT primary key ,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into student(name,email,age) values('张三','zhangsan@126.com',22);
insert into student(name,email,age) values('李四','lisi@126.com',21);
insert into student(name,email,age) values('王五','wangwu@163.com',22);
insert into student(name,email,age) values('赵六','zhaoliun@qq.com',24);
select * from student;
2、新建maven项目。
3、修改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>5.1.32</version>
</dependency>
4、修改pom.xml文件,添加资源文件指定。
<build>
<resources>
<!--将src/main/resources目录下的所有资源文件打包-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<!—filtering 选项 false 不启用过滤器, *.property 已经起到过滤的作用了 -->
<filtering>false</filtering>
</resource>
<!--同时也将src/main/java目录下的xml文件打包-->
<resource>
<directory>src/main/java</directory>
<includes>
<!-- **表示java路径下的所有目录(文件夹),*.xml表示所有xml文件-->
<include>**/*.xml</include>
</includes>
<!—filtering 选项 false 不启用过滤器, *.property 已经起到过滤的作用了 -->
<filtering>false</filtering>
</resource>
</resources>
</build>
5、在idea中添加数据库的可视化:
6、添加jdbc.properties属性文件(数据库的配置):
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
// com.mysql.jdbc.Driver是mysql-connector-java 5版本的驱动名称,5版本之后替换为com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
7、添加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>
<!--读取属性文件(jdbc.properties)
属性:
<properties resource="xxx" 或 url="xxx"></properties>
resources:从resources目录下找指定名称的文件加载
url:使用绝对路径加载属性文件
-->
<properties resource="jdbc.properties"></properties>
<!--设置日志输出底层执行的代码-->
<!--name和value是固定的,STDOUT_LOGGING 表示控制台日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--注册实体类的别名,注册完后Mapper文件就可以使用别名代替完整类名-->
<!--别名大小写不敏感-->
<typeAliases>
<!--单个实体类别名注册-->
<!--<typeAlias type="com.bjpowernode.pojo.Student" alias="student"></typeAlias>-->
<!--批量注册别名。因为在公司的项目中一个包下可能有几百个类,如果都注册别名需要写几百行的注册别名,不实用,可以批量注册。
别名是类名的驼峰命名法(规范)
-->
<package name="com.bjpowernode.pojo"></package>
</typeAliases>
<!--配置数据库的环境变量(数据库连接配置)
default:使用下面的environment标签的id属性进行指定配置
-->
<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">
<!--配置数据库连接的基本参数
private String driver;
private String url;
private String username;
private String password;
-->
<property name="driver" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</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:从resources目录下找指定名称的文件注册
url:使用绝对路径注册
class:动态代理方式下的注册
-->
<!-- 需要多少个mapper注册多少个mapper-->
<mappers>
<mapper resource="StudentMapper.xml"></mapper>
</mappers>
<!--
<!--注册mapper.xml文件-->
<mappers>
<!--绝对路径注册-->
<mapper url="/"></mapper>
<!--非动态代理方式下的注册(StudentMapper.xml文件在resources文件夹下)-->
<mapper resource="StudentMapper.xml"></mapper>
<!--动态代理方式下的单个mapper.xml文件注册-->
<mapper class="com.bjpowernode.mapper.UsersMapper"></mapper>
<!--批量注册-->
<package name="com.bjpowernode.mapper"></package>
</mappers>
-->
</configuration>
注意注意注意注意!!!!
完整类名/完全限定名 是从Java文件夹下开始数的而不是src。完全限定名 即 含包名的类名。从src开始的叫类路径!!!!
注意:mybatis配置文件内容和顺序,必须为以下顺序,否则会出现错误
:
<configuration><!--配置-->
<properties/><!--属性-->
<settings/><!--设置-->
<typeAliases/><!--类型别名-->
<typeHandlers/><!--类型处理器-->
<objectFactory/><!--对象工厂-->
<plugins/><!--插件-->
<environments><!--配置环境-->
<environment><!--环境变量-->
<transactionManager/><!--事务管理器-->
<dataSource/><!--数据源-->
</environment>
</environments>
<databaseidProvider/><!--数据库厂商标识-->
<mappers/><!--映射器-->
</configuration>
如果我们想查看访问数据库时底层执行的一个代码,可以设置日志输出底层的代码。
Parameters是SQL语句的入参,如果使用 ${} 则入参不会显示(直接字符串拼接了)(存在SQL注入的风险)。
8、针对所要查询的表格,创建表格的实体类Student,用来封装查询到的数据。
package com.bjpowernode.pojo;
/**
* 实体类Student
*/
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Student(String name, String email, Integer age) {
this.name = name;
this.email = email;
this.age = age;
}
public Student(Integer id, String name, String email, Integer age) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
}
public Student() {
}
}
注意:实体类的所有属性都应该采用包装类而不是基本数据类型。
例如,在一个成绩查询系统中,有的学生会出现考0分,而有的学生缺考,这就出现了0和NULL两种不同的属性值,如果我们采用基本数据类型int,那么即便在数据库中属性值为NULL,在经过Mybatis查询后,如果赋值给int类型也只会赋值0而非NULL,就无法区分0分和缺考的情况。
(防止初始化默认值为0从而与赋值0无法区分)
再者,当我们在使用mybatis的时候,mybatis会判断参数属性是否为空null,而我们实体类中的某个属性如果使用基本数据类型,如int,默认为0,那mybatis就无法对这个参数进行判断。
9、添加完成学生表的增删改查的功能的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文件
属性:
namespace:指定命名空间(相当于包名),用来区分不同mapper.xml文件中相同的id属性
假设我有一个StudentMapper.xml,还有一个TeacherMapper.xml,就需要通过namespace进行区分。
namespace的值可以任意定义。
-->
<mapper namespace="zar">
<!--
完成查询全部学生的功能,类似于Java的方法。
若定义的是Java方法,则方法定义为 List<Student> getALL();
resultType:指定查询返回的结果集的类型。如果返回的是集合,则resultType必须是泛型的类型!!!
因为如果resultType="List<Student>",就相当于拿一个list集合把所有的数据全装进来然后返回,可这样子数据就乱了不能一一对应上了,
所以必须这种情况下必须使用泛型的类型,即Student,一个一个Student装起来返回,而不是一堆一堆Student一股脑返回,避免了混乱的问题。
parameterType:如果有参数,则通过它来指定参数的类型
-->
<select id="getAll" resultType="student" >
select id,name,email,age
from student
</select>
<!--
按主键id查询学生信息
Student getById(Integer id);
-->
<select id="getById" parameterType="int" resultType="student">
select id,name,email,age
from student
where id=#{id}
</select>
<!--
按学生名称模糊查询
List<Student> getByName(String name);
-->
<select id="getByName" parameterType="string" resultType="student">
select id,name,email,age
from student
where name like '%${name}%'
</select>
<!--增删改的方法中,都会带有返回值,返回受影响的行数。默认的,不需要设置,所以增删改没有resultType-->
<!--
增加学生
int insert(Student stu);
实体类:
private Integer id;
private String name;
private String email;
private Integer age;
-->
<insert id="insert" parameterType="student">
<!--不需要#{stu.name},直接写变量即可,系统会自动识别stu对象中的该变量的值。-->
<!--而且在Java程序中实参也只用写一个,就是对象即可,不用“对象.变量”-->
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);
-->
<update id="update" parameterType="student">
<!--不需要#{stu.name},直接写变量即可,系统会自动识别stu对象中的该变量的值。-->
<!--而且在Java程序中实参也只用写一个,就是对象即可,不用“对象.变量”-->
update student set name=#{name},email=#{email},age=#{age}
where id=#{id}
</update>
</mapper>
StudentMapper.xml文件中的SQL语句不需要写分号!
xml文件的如果存在类似 user
name=#{userName} 这样的语句,前一个username是MySQL的列名,因为MySQL不区分大小写,所以name的首字母大小写均可,而后一个userName是实体类中的属性,因为Java的命名规范是驼峰命名法,这就导致了Name的首字母是大写,如果这里不写大写就会出错了!!!!
parameterType:
可以不写。但是多个入参需要在mapper接口中采用注解的方式将参数注解进去。
(数组、集合属于单个入参,可以不写注解不写
parameterType
)(但最好不要嫌麻烦)
(注意,resultType时如果返回集合需要写集合的泛型。但parameterType如果是集合那么类型就是集合,而不是泛型。
resultType写泛型是为了不让对象的属性全都混乱地怼进集合里区分不开,而parameterType是集合的话对象的属性已经整齐地排好了,不会混乱)
resultType简单类型可以省略,其余不可省略。
10、创建测试类,进行功能测试:
package com.bjpowernode.test;
import com.bjpowernode.pojo.Student;
import com.sun.org.apache.bcel.internal.generic.FADD;
import org.apache.ibatis.io.Resources;
// 注意,这个包的org.apache.ibatis.io,别选错了
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
@Before //在所有的@Test方法执行前先执行的代码,简化代码量。
public void openSqlSession() throws IOException {
// 使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// getResourceAsStream()方法自动会去不用Resources配置文件夹下找配置文件,不需要路径,只写名称和后缀即可。
// 创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 取出sqlSession的对象
sqlSession = factory.openSession();
}
@After //在所有的@Test方法执行后先执行的代码,简化代码量。
public void closeSqlSession(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void testGetAll() throws IOException {
//完成查询操作
List<Student> list = sqlSession.selectList("zar.getAll");
list.forEach(student -> System.out.println(student));
}
@Test
public void testGetById() throws IOException {
//按主键查学生
//第二个是实参
Student stu = sqlSession.selectOne("zar.getById",3);
System.out.println(stu);
}
@Test
public void testGetByName()throws IOException{
//按名字模糊查询
//第二个是实参
List<Student> list = sqlSession.selectList("zar.getByName","李");
list.forEach(student -> System.out.println(student));
}
@Test
public void testInsert()throws IOException{
//插入数据
// 配置文件中不需要#{stu.name},直接写变量即可,系统会自动识别stu对象中的该变量的值。而在Java程序中实参也只用写一个,就是对象即可,不用“对象.变量”
int num = sqlSession.insert("zar.insert",new Student("haha666","haha@126.com",23));
// 因为这里利用了主键的自增,即不手动给主键赋值,所以实体类中必须有一个无主键属性的构造方法
//切记切记切记:在所有的增删改后必须手工提交事务!!!
sqlSession.commit();
}
@Test
public void testDelete()throws IOException {
//按id删除数据
int num = sqlSession.delete("zar.delete",1);
System.out.println(num);
//切记切记切记:在所有的增删改后必须手工提交事务!!!
sqlSession.commit();
}
@Test
public void testUpdate()throws IOException {
//更新数据
// 配置文件中不需要#{stu.name},直接写变量即可,系统会自动识别stu对象中的该变量的值。而在Java程序中实参也只用写一个,就是对象即可,不用“对象.变量”
int num = sqlSession.update("zar.update",new Student(3,"hehe","hehe@126.com",30));
System.out.println(num);
sqlSession.commit();
}
}
根据查询的返回值数量选中调用不同的方法,根据SQL语句的类型(增删改查)选中不同的方法。
Mybatis的dtd文件解读:
dtd文件是xml文件的写作规范的规定。在xml文件的头部均有该xml文件的dtd文件的超链接指向。
dtd文件的规则定义:
定义开始标签和结束标签间的内容:
<!ELEMENT 标签名 (当前标签内可定义的内容xxx)> 标签:
dtd文档中的第一个,代表xml中的根标签。其中:xxx可以定义的内容有:
1、子标签:
如:(标签名?,标签名*,标签名+,标签名,(标签名|标签名)+)。其中逗号”,“代表子标签的定义顺序;
问号”?“代表子标签可以定义0或1个;
星号”“代表子标签可以定义0或n个;
加号”+“代表子标签可以定义1或n个;
标签名后不加数量符号,代表只能定义唯一一个,且必须定义,括号代表优先级;
竖线”|”代表选择。
2、空标签
:用 EMPTY 表示,代表当前标签是一个空标签,不能再定义子标签。如:
mapper标签:
<!--dtd文件中--> <!ELEMENT mapper EMPTY> <!ATTLIST mapper resource CDATA #IMPLIED url CDATA #IMPLIED class CDATA #IMPLIED > <!--xml文件中--> <mappers> <mapper resource="StudentMapper.xml"></mapper> </mappers>
3、#PCDATA:
代表当前标签没有子标签,但是可以定义字符类型的标签体,如:spring中的value标签abc。
注意:dtd文件中的 ELEMENT 标签内定义了标签顺序(前后),在xml文件中使用标签时必须按照顺序填写标签,不然会报错。
定义写在开始标签尖括号内的内容:
<!ATTLIST 标签名 属性名 属性特征 属性名 属性特征> :
标签的属性列表。属性特征:(可联合使用)
1、CDATA:
字符类型,属性值不能包含空格、回车、换行、制表符。
2、a|b|c:
枚举,代表当前属性的可选值有哪些
3、#IMPLIED:
可选属性
4、#REQUIRED:
必要属性
5、default “数据”:
代表当前属性默认值是什么。
6、NMTOKEN:
字符类型,可以包含空格。代表多个字符串。<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*> <!ATTLIST select id CDATA #REQUIRED parameterMap CDATA #IMPLIED parameterType CDATA #IMPLIED resultMap CDATA #IMPLIED resultType CDATA #IMPLIED resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE | DEFAULT) #IMPLIED statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED fetchSize CDATA #IMPLIED timeout CDATA #IMPLIED flushCache (true|false) #IMPLIED useCache (true|false) #IMPLIED databaseId CDATA #IMPLIED lang CDATA #IMPLIED resultOrdered (true|false) #IMPLIED resultSets CDATA #IMPLIED > <!--id是必写的,而返回值类型是可选的--> <select id="getAll" resultType="student" > select id,name,email,age from student </select>
驼峰映射:
驼峰映射,就是将满足sql下划线的标准写法字段自动转换为满足java命名规则的实体属性,不需要用resultMap自己建立映射关系。
如user数据表字段 : u_id , u_name,u_sex;
会自动转换为User类属性匹配的字段:uId,uName,uSex;
在SqlMapConfig.xml文件中:
<!--
开启驼峰写法 自动将数据库表字段 s_address 映射为Java属性 sAddress
<setting name="mapUnderscoreToCamelCase" value="true"/>
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
Mapper文件实参、返回值类型别名:
在mybatis中TypeAliasRegistry已经为我们配置的默认别名。
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
如果我们需要实参/返回值Integer类型时,只用输入int即可。
<delete id="delete" parameterType="int" >
delete from student where id=#{id}
</delete>
MyBatis对象分析:
Resources 类
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析SqlMapConfig.xml文件,返回不同类型的 IO 流对象。
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder 类
SqlSessionFactory 的创建 , 需要使用 SqlSessionFactoryBuilder 对象的 build() 方法 。 由于SqlSessionFactoryBuilder对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 对象创建为一个方法内的局部对象,方法结束,对象销毁。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSessionFactory 接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
- openSession(true):创建一个有自动提交功能的 SqlSession
- openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
- openSession():同 openSession(false)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession 接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。
SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。
sqlSession = factory.openSession();
Mybatis中动态代理存在意义:
在三层架构中,业务逻辑层要通过接口访问数据访问层的功能。传统分层模式为了耦合度低,都会用接口指向实现类的对象。这就导致了我们在处理业务的时候需要写 dao 层(数据访问层),然后再写 dao层的实现类,在实现类中通过sqlSeason对象对底层数据库进行访问。而 mybatis 在动态代理之后,在只写 dao层的接口(Maper接口)的情况之下,可以直接指向xml文件,mybatis 就会自动生成 dao的实现类 (Mapper接口),就可以调用 dao的接口了(Mapper实现类)。
并且,在阅读源码之后我们还可以发现,Mybatis的动态代理属于JDK动态代理。
动态代理的实现规范:
(1)UsersMapper.xml文件与UsersMapper.java的接口必须同一个目录下。
(2)UsersMapper.xml文件与UsersMapper.java的接口的文件名必须一致,后缀不管。
(3)UsersMapper.xml文件中标签的id值与sMapper.java的接口中方法的名称完全一致。
(4)UsersMapper.xml文件中标签的parameterType属性值与UsersMapper.java的接口中方法的参数类型完全一致。
(5)UsersMapper.xml文件中标签的resultType值与UsersMapper.java的接口中方法的返回值类型完全一致。
(6)UsersMapper.xml文件中namespace属性必须是接口的完全限定名称com.bjpowernode.mapper.UsersMapper。
(7)在SqlMapConfig.xml文件中注册mapper文件时,使用class=接口的完全限定名称com.bjpowernode.mapper.UsersMapper。
动态代理情况下mapper.xml文件的注册:
<!--注册mapper.xml文件-->
<mappers>
<!--绝对路径注册-->
<mapper url="/"></mapper>
<!--非动态代理方式下的注册(StudentMapper.xml文件在resources文件夹下)-->
<mapper resource="StudentMapper.xml"></mapper>
<!--动态代理方式下的单个mapper.xml文件注册-->
<mapper class="com.bjpowernode.mapper.UsersMapper"></mapper>
<!--批量注册-->
<package name="com.bjpowernode.mapper"></package>
</mappers>
注意注意注意注意!!!!
完整类名/完全限定名 是从Java文件夹下开始数的而不是src。完全限定名 即 含包名的类名。从src开始的叫类路径!!!!
Mybatis动态代理代码示例:
jdbc.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
UsersMapper接口:
package com.bjpowernode.mapper;
import com.bjpowernode.pojo.Users;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 数据访问层的接口,规定的数据库中可进行的各种操作
*/
public interface UsersMapper {
//查询全部用户信息
List<Users> getAll();
//根据用户主键查用户
Users getById(Integer id);
//根据用户名模糊查询用户
List<Users> getByName(String name);
//优化后的模糊查询
List<Users> getByNameGood(String name);
//用户的更新
int update(Users users);
//增加用户
int insert(Users users);
//根据主键删除用户
int delete(Integer id);
//模糊用户名和地址查询
List<Users> getByNameOrAddress(
@Param("columnName")
String columnName,
@Param("columnValue")
String columnValue);
}
UsersMapper.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 namespace="com.bjpowernode.mapper.UsersMapper">
<!--
//查询全部用户信息
List<Users> getAll();
-->
<select id="getAll" resultType="users" >
select id,username,birthday,sex,address
from users
</select>
<!--
//根据用户主键查用户
Users getById(Integer id);
-->
<select id="getById" parameterType="int" resultType="users">
select id,username,birthday,sex,address
from users
where id=#{zar}
</select>
<!--
//根据用户名模糊查询用户
List<Users> getByName(String name);
-->
<select id="getByName" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like '%${zar}%'
</select>
<!--
//用户的更新
int update(Users users);
private Integer id;
private String userName;
private Date birthday;
private String sex;
private String address;
-->
<update id="update" parameterType="users" >
update users set username = #{userName},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
<!--
//增加用户
int insert(Users users);
-->
<insert id="insert" parameterType="users" >
insert into users (username, birthday, sex, address) values(#{userName},#{birthday},#{sex},#{address})
</insert>
<!--
//根据主键删除用户
int delete(Integer id);
-->
<delete id="delete" parameterType="int" >
delete from users
where id=#{id}
</delete>
</mapper>
SqlMapConfig:
<?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>
<!--读取jdbc.properties属性-->
<properties resource="jdbc.properties"></properties>
<!--设置日志输出-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--注册实体类的别名-->
<typeAliases>
<package name="com.bjpowernode.pojo"></package>
</typeAliases>
<!--配置环境变量-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--
private String driver;
private String url;
private String username;
private String password;
-->
<property name="driver" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!--注册mapper.xml文件-->
<mappers>
<!--单个注册-->
<!--<mapper class="com.bjpowernode.mapper.UsersMapper"></mapper>-->
<!--批量注册-->
<!--com.bjpowernode.mapper下的所以xml文件都会被注册-->
<package name="com.bjpowernode.mapper"></package>
</mappers>
</configuration>
pom.xml:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>mybatis_002_users</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--添加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.29</version>
</dependency>
</dependencies>
<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>
</project>
Users:
package com.bjpowernode.pojo;
import java.util.Date;
/**
*
*/
public class Users {
private Integer id;
private String userName;
private Date birthday;
private String sex;
private String address;
@Override
public String toString() {
return "Users{" +
"id=" + id +
", userName='" + userName + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Users(String userName, Date birthday, String sex, String address) {
this.userName = userName;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public Users(Integer id, String userName, Date birthday, String sex, String address) {
this.id = id;
this.userName = userName;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public Users() {
}
}
MyTest:
package com.bjpowernode.test;
import com.bjpowernode.mapper.UsersMapper;
import com.bjpowernode.pojo.Users;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
//动态代理对象
UsersMapper uMapper;
//日期的格式化刷子
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
@Before
public void openSqlSession() throws IOException {
//1.读取核心配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.取出sqlSession
sqlSession = factory.openSession(true);//自动提交事务
//4.取出动态代理的对象,完成接口中方法的调用,实则是调用xml文件中相对应的标签的功能
uMapper = sqlSession.getMapper(UsersMapper.class);
}
@After
public void closeSqlSession(){
sqlSession.close();
}
@Test
public void testGetAll(){
System.out.println(uMapper.getClass());
//就是在调用接口的方法,mybatis框架已经为我们把功能代理出来了.
List<Users> list = uMapper.getAll();
list.forEach(users -> System.out.println(users));
}
@Test
public void testUpdate() throws ParseException {
Users u = new Users(7,"haha66",sf.parse("2000-01-01"),"2","北京大兴亦庄66");
int num = uMapper.update(u);
System.out.println(num);
//切记切记切记:手工提交事务
sqlSession.commit();
}
@Test
public void testById(){
Users u = uMapper.getById(27);
System.out.println(u);
}
@Test
public void testGetByName(){
List<Users> list = uMapper.getByName("小");
list.forEach(users -> System.out.println(users));
}
@Test
public void testInsert() throws ParseException {
Users u = new Users("lala66",sf.parse("2001-01-01"),"2","大兴");
int num = uMapper.insert(u);
System.out.println(num);
sqlSession.commit();
System.out.println(u);
}
@Test
public void testDelete() throws ParseException {
int num = uMapper.delete(3);
System.out.println(num);
// sqlSession.commit();
}
}
#{}与${}的区别:
1、#{}是
预编译
处理,是占位符,${}是字符串替换,是拼接符。
2、Mybatis在处理#{}的时候会将sql中的#{}替换成 ? 号,调用PreparedStatement来赋值。
3、Mybatis在处理${}的时候就是把${}替换成变量的值,调用Statement来赋值。。
4、#{}的变量替换是在DBMS中、变量替换后,#{}对应的变量自动加上单引号。( 导致了有些地方必须使用${} )
5、${}的变量替换是在DBMS外、变量替换后,${}对应的变量不会加上单引号。
6、使用#{}可以有效的防止sql注入,提高系统的安全性。
7、当实参是对象时,#{}可以读取到对象内部的属性值,而${}不可以。
#{}占位符:
传参大部分都使用 #{} 传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问,可以防止sql注入。
#{}里如何写,看parameterType参数的类型:
(1)如果parameterType的类型是简单类型(8种基本数据类型(包括封装类)+String),则#{}里随便写,类似于形参。
<select id="getById" parameterType="int" resultType="users"> ===>入参类型是简单类型
select id,username,birthday,sex,address
from users
where id=#{zar} ===>随便写
</select>
(2)parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写。
<insert id="insert" parameterType="users" > ===>入参是实体类
insert into users (username, birthday, sex, address) values(#{userName},#{birthday},#{sex},#{address}) ==>成员变量名称
</insert>
#{}占位符不能解决的三类问题:
表名不能够用#{} :Select * from #{table}
列名不能够用#{} : select #{column} from table
排序不能够用#{} : select * from table order by #{column}
${}字符串拼接:
字符串拼接,一般用于模糊查询中。建议少用,因为有sql注入的风险。
也分两种情况,同样的看parameterType的类型。
(1)如果parameterType的类型是简单类型,则${}里随便写,但是分版本,如果是3.5.1及以下的版本,只以写value。
<select id="getByName" parameterType="string" resultType="users"> ===>入参是简单类型
select id,username,birthday,sex,address
from users
where username like '%${zar}%' ===>随便写
</select>
(2)如果parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称。(现在已经少用)
当模糊查询时,因为#{}会自动加上单引号,这就导致了会SQL语法错误,所以必须使用${}。即
‘%${zar}%’
。
但可以使用 concat()函数与#{}进行搭配 从而规避使用${}会导致SQL注入的风险。
而当查询条件的value不是模糊查询时则可以使用#{}。
优化后的模糊查询(以后都要使用这种方式):
<select id="getByNameGood" parameterType="string" resultType="users">
select id,username,birthday,sex,address
from users
where username like concat('%',#{name},'%')
</select>
${}字符串替换:
现有需求:模糊地址或用户名查询。
SQL语句:
select * from users where username like '%小%';
select * from users where address like '%市%'
UsersMapper接口:
//模糊用户名和地址查询
List<Users> getByNameOrAddress(
@Param("columnName")
String columnName,
@Param("columnValue")
String columnValue);
UsersMapper.xml:
<!--
//模糊用户名和地址查询
//如果参数超过一个,则parameterType不写
List<Users> getByNameOrAddress(
@Param("columnName")
String columnName,
@Param("columnValue")
String columnValue);
-->
<select id="getByNameOrAddress" resultType="users">
select id,username,birthday,sex,address
from users
where ${columnName} like concat('%',#{columnValue},'%')
</select>
注意,此处不能用 #{} :
mysql> select username from users where 'id'=1;
Empty set, 1 warning (0.00 sec) // 查询为空集,因为没有 'id' 字段,只有 id 字段。
// 查询时select、from等语句后面都不可以跟单引号,会导致查询结果有差异,需要使用${},且在这种情况下不会导致sql注入,因为注入后sql语法不正确。而where、like等语句后就可以使用#{}。
@Param注解:
在实际的开发中,经常会遇到多个接口参数的情况。除了将不同的参数封装在一个对象内,还可以使用@param注解的方式传递参数名称。
使用@Param注解来声明参数的时候,SQL语句取值使用#{},${}取值都可以。
使用@Param注解来声明参数的时候,可以不写parameterType。
@注解
简单类型(8种基本数据类型(包括封装类)+String):
dao层示例:
Public User selectUser(@param(“userName”) String name, @param(“userpassword”) Int password);
xml映射对应示例:
<!--使用注解传递参数,这时是不涉及单独一个类型的,所以去掉parameterType属性-->
<select id=" selectUser" resultMap="BaseResultMap">
select * from user_user_t
where user_name = #{userName} and user_password=#{userPassword}
</select>
注意:采用#{}的方式把@Param注解括号内的参数进行引用(括号内参数对应的是形参如 userName对应的是name),而xml映射中#{}或${}内写的并非是形参的名称而是引用其的@Param()内的名称。
在这里给参数配置@param注解后,Mybatis会自动将参数封装成Map类型,而@param注解的值会成为Map中的key,因此在sql中可以通过配置的注解值来使用参数。
@Param注解JavaBean对象:
dao层示例:
Users test(@Param("users") Users users);
xml映射对应示例:
<select id="test" resultType="users">
select id,username,birthday,sex,address
from users
where username = #{users.userName}
</select>
测试类:
@Test
public void tsetTest() throws ParseException {
Users users = uMapper.test(new Users("张三",new Date(),"2","随便,因为只取userName"));
}
可以看到,经过@param配置后的参数在xml文件的sql中可以直接使用。但是当参数是JavaBean类型时,使用@param注解后传过来的仅仅只是一个对象,对象内的属性要通过点取值的方式提取出来。
使用@Param注解才需要用“点”的方式取出对象中的属性,如果不使用@Param注解可以直接#{}获取。
插入返回主键值:
业务:当A表更新一条数据(主键id不手动赋值而是采用主键自增的方式让MySQL赋默认值),取B表给A表新增的数据的id的积分增加500分。
分析,这个业务如果采用传统的方式,就得在更新之后再次访问数据库读取新增的数据对应的主键id,增加了数据库的访问次数,性能有所下降。这时我们可以采用Mybatis的<selectKey>标签和 MySQL的select last_insert_id() 方法让新增的主键值返回并赋值给 传来的新增的对象的id属性。
使用 JDBC 方式返回主键自增的值:
Mapper.xml 伪代码:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert语句
</insert>
useGeneratedKeys 设置为true后,MyBatis 会使用 JDBC的getGeneratedKeys 方法来取出由数据库内部生成的主键。获取主键值后将其赋值给 keyProperty 配置的 id 属性。
使用 selectKey 返回主键的值:
有些数据库(如 Oracle)不提供主键自增的功能,而是使用序列得到一个值,然后将这个值赋给 id,再将数据插入数据库。这种情况可以是 <selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。
MySQL 数据库对应Mapper.xml 伪代码:
<insert id="insert">
insert 语句
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
Oracle 数据库对应 Mapper.xml 伪代码:
<insert id="insert">
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE">
SELECT SEQ_ID.nextval from dual
</selectKey>
insert 语句
</insert>
<selectKey>标签的参数详解:
keyProperty:users对象的哪个属性来接返回的主键值
resultType:返回的主键的类型
order:在插入语句执行前,还是执行后返回主键的值,有BEFORE和AFTER两个属性。
selectKey标签位置不一样不会影响 selectKey 中的方法 在 insert 前面或者后面执行的顺序,影响执行顺序的是 order 属性。
-
MySQL 是先插入数据才能获取自增主键,所以selectKey在insert后执行,order属性为 AFTER。
-
Oracle 是先从序列中取出主键值然后插入到数据库中,所以 selectKey 在 insert 前执行,order 属性为 BEFORE。
UUID:
当我们的项目需要给一个属性全球唯一的值,那么我们可以采用UUID获得该值。
Java有UUID这个方法,返回一个随机的全球唯一的字符串,一个由36个字母、数字和下划线组成的字符串。
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString())
System.out.println(uuid.toString().replace("-","").substring(20));
// 转为字符串后可以对其使用一些字符串的函数,可以去除 - ,或截取长度等
MySQL也提供了这样一个方法:
select uuid();
动态sql:
代码复用:
<sql>:用来定义代码片段,可以将所有的列名,或复杂的条件定义为代码片段,供使用时调用。
<include>:用来引用<sql>定义的代码片段。
<!--定义代码片段-->
<sql id="allColumns">
id,username,birthday,sex,address
</sql>
<!--引用定义好的代码片段-->
<select id="getAll" resultType="users" >
select <include refid="allColumns"></include>
from users
</select>
动态查询:
根据传过来的对象所持有的属性值对数据库进行属性模糊查询。
<if>:进行条件判断:
test条件判断的取值可以是实体类的成员变量,可以是map的key,可以是@Param注解的名称。
<where>:进行多条件拼接,在查询,删除,更新中使用。
如:
<!--
//按指定的条件进行多条件查询
List<Users> getByCondition(Users users);
根据实体类中的成员变量是否有值来决定是否添加条件
private Integer id; null
private String userName; null
private Date birthday; null
private String sex; null
private String address; null
private int num; 0
private boolean flag; false
private double d ; 0.0
如果是int就要判断是不是0,以此类推。
-->
<!--当没有条件时底层SQL语句就不会出现where关键字,只有一个条件时会自动把and去掉,大于一个条件时才会在两条件之间加and-->
<!--字符串都要判断null串和空串,但Date类型只用判断null串,不可能为空串-->
<select id="getByCondition" parameterType="users" resultType="users">
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>
该代码会根据所传过来的users对象所含的非默认值的属性进行查询,字符串会模糊查询。
动态修改:
如果我想修改id为8的人的name属性:
测试类中:
@Test
public void testUpdate() throws ParseException {
Users u = new Users();
u.setId("8");
u.setUserName("hyf");
uMapper.update(u);
sqlSession.commit();
}
UsersMapper.xml文件中:
<update id="update" parameterType="users" >
update users set username = #{userName},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
因为没有给其他属性赋值,所以其他属性是默认值, 而UsersMapper.xml文件中是对所有属性进行修改,当方法执行时,原id为8的数据的除name以外的其他属性都会被赋上默认值,原先的数据就都丢失了。
为了避免这种情况,我们就可以使用<set>标签,只对传来的对象中的非null且非空的数据进行更新。
<set>:有选择的进行更新处理,至少更新一列。
<!--
//根据id有选择的更新
int updateBySet(Users users);
-->
<!--
if内的每条更新语句最后(或前面)一定要加上逗号,mybatis会自动删掉最后一个(或第一个)更新属性的逗号从而让SQL语句正确
update users
set username = #{userName},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
-->
<update id="updateBySet" parameterType="users">
update users
<set>
<if test="userName != null and userName != ''">
username = #{userName},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex != null and sex != ''">
sex = #{sex},
</if>
<if test="address != null and address != ''">
address =#{address} ,
</if>
</set>
where id = #{id}
</update>
如果除了id之外其他都不手动赋值(也就是说一列都不更新),那么底层的SQL语句中就没了set关键字,即update users where id = #{id} ,很明显就会报错,所以至少要更新一列(一个属性)。(如果一个都不更新那干嘛要调用更新语句?说不过去)
Foreach标签:
用来进行循环遍历,完成批量查询,批量删除,批量增加,批量更新。
批量查询:
我们要查询一次多个id时,在xml文件中SQL语句可以这样写:
select <include refid="allColumns"></include>
from users
where id=2 or id=4 or id=6
而or有一个简单的写法:
select <include refid="allColumns"></include>
from users
where id in (2,4,6)
正是因为有这个特性,我们可以在括号内用foreach语句对数组进行遍历输出从而批量查询多个id。
parameterType超过一个都可以不写,所以数组/集合也可以不写。(其实简单类型也可以不写,即8个基本数据类型、8个包装类以及String)
<!--
//查询多个指定id的用户信息
List<Users> getByIds(Integer []arr);
-->
<!--
item是定义foreach所取出来的元素每一个的名称,可任意写,但下面的#{}内的名称需要与item所定义的名称一致。
separator是每个取出来的元素间的分隔符(如果手动在#{id}前或后加的话会导致多一个逗号而SQL语法错误)
open="(" 是指foreach语句前是一个( ,从而省略foreach前的( ,这样可以让xml文件中跟整齐一些
close=")" 是指foreach语句前是一个) ,从而省略foreach前的) ,这样可以让xml文件中跟整齐一些
-->
<select id="getByIds" resultType="users">
select <include refid="allColumns"></include>
from users
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<foreach>参数详解:
collection:用来指定入参的类型,如果是List集合,则为list,如果是Map集合,则为map,如果是数组,则为array。
item:每次循环遍历出来的值或对象的名称(局部变量名,可任意,但下面的#{}内的名称需要与item所定义的名称一致。)。(如果取出来的是对象,那么需要通过 “对象.变量” 的形式取出值)
separator:多个值或对象或语句之间的分隔符。
open:foreach:代替循环外面的前括号(使得原先循环外的括号可以省略)(不可替代循环内的括号) 。
close:foreach:代替循环外面的后括号(使得原先循环外的括号可以省略)(不可替代循环内的括号) 。
批量删除:
同批量查询。
<!--
//批量删除
int deleteBatch(Integer []arr);
-->
<delete id="deleteBatch" >
delete from users
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
批量增加:
<!--
//批量增加
int insertBatch(List<Users> list);
private Integer id; null
private String userName; null
private Date birthday; null
private String sex; null
private String address; null
-->
<insert id="insertBatch">
insert into users(username, birthday, sex, address) values
<foreach collection="list" item="u" separator="," >
(#{u.userName},#{u.birthday},#{u.sex},#{u.address})
</foreach>
</insert>
批量修改:
注意:要使用批量更新,必须在
jdbc.properties
属性文件中的
url
中添加
&allowMultiQueries=true
,允许多行操作。
之前的是一个SQL语句影响多行(插入),但批量更新是多条SQL语句影响多行,所以要开启。
<!-- 有选择的批量更新,至少更新一列-->
<update id="updateSet" >
<foreach collection="list" item="u" separator=";">
update users
<set>
<if test="u.userName != null and u.userName != ''">
username=#{u.userName},
</if>
<if test="u.birthday != null">
birthday = #{u.birthday},
</if>
<if test="u.sex != null and u.sex != ''">
sex = #{u.sex},
</if>
<if test="u.address != null and u.address != ''">
address = #{u.address}
</if>
</set>
where id = #{u.id}
</foreach>
</update>
foreach对整一条更新语句进行重复,且每一条更新语句中都进行动态SQL确定所要更改的属性。
@Test
public void testUpdateBatch()throws Exception{
List<Users> list = new ArrayList<>();
Users u1 = new Users(3,"张1167",new SimpleDateFormat("yyyy-MM-dd").parse("1997-02-03"),"2","北京大兴亦庄1111");
Users u2 = new Users(4,"李2267",new SimpleDateFormat("yyyy-MM-dd").parse("1998-02-03"),"1","北京大兴亦庄2333");
Users u3 = new Users(5,"王3367",new SimpleDateFormat("yyyy-MM-dd").parse("1999-02-03"),"2","北京大兴亦庄122");
list.add(u1);
list.add(u2);
list.add(u3);
int num = mapper.updateSet(list);
session.commit();
System.out.println(num);
}
指定参数位置:
如果入参是多个,除了使用@param注解之外,还可以使用指定参数位置,参数位置下标从0开始,3.4.1版本之前可以直接写 #{参数下标} ,3.4.1版本之后必须写 #{arg参数下标} 。
例如:查询指定日期范围内的用户信息。
<!--
//查询指定日期范围内的用户
List<Users> getByBirthday(Date begin, Date end);
-->
<select id="getByBirthday" resultType="users">
select <include refid="allColumns"></include>
from users
where birthday between #{arg0} and #{arg1}
</select>
入参是map(重点掌握) :
如果实参中有数量的起始和终止、价格的起始和终止、日期的起始和终止等,继续用指定参数位置的方式的话,下标全是数字,没有明确的语义,可读性很差。而如果使用@param则每一个实参都要定义一下,很麻烦。
所以如果入参超过一个以上,建议使用map封装查询条件,更有语义,查询条件更明确。
UsersMapper接口:
List<Users> getByMap(Map map);
UsersMapper.xml文件:
<!--
//入参是map
#{birthdayBegin} 对应的就是map中的key,取出来的就是对应的值value。
-->
<select id="getByMap" resultType="users" >
select <include refid="allColumns"></include>
from users
where birthday between #{birthdayBegin} and #{birthdayEnd}
</select>
测试类中:
@Test
public void testGetByMap() throws ParseException {
Date begin = sf.parse("1999-01-01");
Date end = sf.parse("1999-12-31");
Map map = new HashMap<>();
map.put("birthdayBegin",begin);
map.put("birthdayEnd", end);
List<Users> list = uMapper.getByMap(map);
list.forEach(users -> System.out.println(users));
}
返回值为一行map:
如果返回的数据实体类无法包含,可以使用map返回多张表中的若干数据。
返回的map的key就是列名(SQL查询时列名起别名则别名是key),对应的value是属性的值。每一个属性就是一个key,该key对应的value就是该属性的值。
业务:根据id属性查询username属性和address属性。
UsersMapper接口:
//返回值是map(一行)
Map getReturnMap(Integer id);
UsersMapper.xml文件:
<!--
//返回值是map(一行)
Map getReturnMap(Integer id);
-->
<select id="getReturnMap" parameterType="int" resultType="map">
select username nam,address a
from users
where id=#{id}
</select>
测试类:
@Test
public void testReturnMapOne(){
Map map = uMapper.getReturnMap(5);
System.out.println(map);
System.out.println(map.size());
}
返回值为多行map:
若在返回值为一行map的基础之上,查询的值不止一个。可以用一个List集合把Map集合装起来。
每一个属性就是一个key,该key对应的value就是该属性的值。每一个Map就是一行。
把map当成一个对象装到list里面,一个map含一行数据(usersname+address)。
UsersMapper接口:
//返回多行的map
List<Map> getMulMap();
UsersMapper.xml文件:
<!--
//返回多行的map
List<Map> getMulMap();
-->
<select id="getMulMap" resultType="map">
select username,address
from users
</select>
resultType为map,因为返回的最下单位为map,如果为list,那么数据会被打乱。
测试类:
@Test
public void testReturnMapMul(){
List<Map> list = uMapper.getMulMap();
list.forEach(map -> System.out.println(map));
}
列名和类中成员变量名称不一致:
数据库:
BookMapper接口:
public interface BookMapper {
//查询全部图书
List<Book> getAll();
}
BookMapper.xml文件:
<select id="getAll" resultType="book">
select bookid,bookname
from book
</select>
Book类:
测试类:
package com.bjpowernode.test;
import com.bjpowernode.mapper.BookMapper;
import com.bjpowernode.mapper.CustomerMapper;
import com.bjpowernode.mapper.OrdersMapper;
import com.bjpowernode.pojo.Book;
import com.bjpowernode.pojo.Customer;
import com.bjpowernode.pojo.Orders;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
BookMapper bookMapper;
@Before //在所有的@Test方法执行前先执行的代码
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//取出sqlSession的对象
sqlSession = factory.openSession();
bookMapper = sqlSession.getMapper(BookMapper.class);
}
@After
public void closeSqlSession(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void testGetAll(){
List<Book> list = bookMapper.getAll();
list.forEach(book -> System.out.println(book));
}
}
很明显,列名跟实体类的成员变量名称不一致。(xml文件中须采用列名作为查询名,因为xml文件直接就对应了底层的SQL语句)
运行结果:
当列名跟实体类的成员变量名称不一致时,程序无法直接获得数据库的值,这个过程不会报错,但取不出来任何值,因为缺少列名与实体类的成员变量之间的一个映射关系。
使用别名进行成员变量和列名的映射:
只需在原始案例的基础之上,为查询的列名起别名即可,别名须跟实体类中成员变量的名称一致。
<select id="getAll" resultType="book">
select bookid id,bookname name
from book
</select>
使用resultMap进行成员变量和列名的映射:
<!--
使用resultMap手工完成映射
type的值就是所要返回的实体类的名称,就是返回值(一个对象)的类型。
-->
<resultMap id="bookmap" type="book">
<!--主键绑定-->
<id property="id" column="bookid"></id>
<!--非主键绑定-->
<result property="name" column="bookname"></result>
</resultMap>
<select id="getAll" resultMap="bookmap">
select bookid,bookname
from book
</select>
property表示类中的变量名,column表示数据库中列名,type表示所要返回的实体类名称。
表之间的关联关系:
关联关系是有方向的。
(1)一对多关联:一个老师可以教多个学生,多个学生只有一个老师来教,站在老师方,就是一对多关联。
(2)多对一关联:一个老师可以教多个学生,多个学生只有一个老师来教,站在学生方,就是多对一关联。
(3)一对一关联:一个老师辅导一个学生,一个学生只请教一个老师。学生和老师是一对一。
(4)多对多关联:园区划线的车位和园区的每一辆车。任意一个车位可以停任意一辆车。任意一车辆车可以停在任意一个车位上。
一对多关联关系:
客户和订单就是典型的一对多关联关系。一个客户名下可以有多个订单。
使用一对多的关联关系,可以满足查询客户的同时查询该客户名下的所有订单。
数据:
use ssm;
Create table customer(
id int primary key auto_increment,
name varchar(32),
age int
);
insert into customer values(1,'张三',22);
insert into customer values(2,'李四',23);
insert into customer values(3,'王五',24);
Create table orders(
id int primary key auto_increment,
orderNumber varchar(16),orderPrice double,
customer_id int ,
foreign key (customer_id) references customer(id)
);
insert into orders values(11,20,22.22,1);
insert into orders values(12,60,16.66,1);
insert into orders values(13,90,19.99,2);
select * from customer;
select * from orders;
Customer实体类:
package com.bjpowernode.pojo;
import java.util.List;
/**
*
*/
public class Customer {
//customer表中的三个列
private Integer id;
private String name;
private Integer age;
// 用一个list集合装该客户名下的所有订单
private List<Orders> ordersList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", ordersList=" + ordersList +
'}';
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<Orders> getOrdersList() {
return ordersList;
}
public void setOrdersList(List<Orders> ordersList) {
this.ordersList = ordersList;
}
public Customer(Integer id, String name, Integer age, List<Orders> ordersList) {
this.id = id;
this.name = name;
this.age = age;
this.ordersList = ordersList;
}
public Customer() {
}
}
Orders实体类:
package com.bjpowernode.pojo;
/**
*
*/
public class Orders {
private Integer id;
private String orderNumber;
private Double orderPrice;
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", orderNumber='" + orderNumber + '\'' +
", orderPrice=" + orderPrice +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public Double getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(Double orderPrice) {
this.orderPrice = orderPrice;
}
public Orders(Integer id, String orderNumber, Double orderPrice) {
this.id = id;
this.orderNumber = orderNumber;
this.orderPrice = orderPrice;
}
public Orders() {
}
}
CustomerMapper接口:
package com.bjpowernode.mapper;
import com.bjpowernode.pojo.Customer;
/**
*
*/
public interface CustomerMapper {
//根据客户的id查询客户所有信息并同时查询该客户名下的所有订单
Customer getById(Integer id);
}
CustomerMapper.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 namespace="com.bjpowernode.mapper.CustomerMapper">
<!--
//根据客户的id查询客户所有信息并同时查询该客户名下的所有订单
Customer getById(Integer id)
实体类:
//customer表中的三个列
private Integer id;
private String name;
private Integer age;
//该客户名下的所有订单的集合
private 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>
<!--多出来的一咕噜绑定ordersList
Orders实体类:
private Integer id;
private String orderNumber;
private Double orderPrice;
-->
<!--collection是集合,表示把数据装进集合内,该标签下定义集合中成员变量与列名之间的映射关系-->
<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>
</mapper>
property表示类中的变量名,column表示数据库中列名,type表示所要返回的实体类名称。
<collection property=”pojo的集合属性” ofType=”集合中的pojo对象”>
<association property=”对象名” javaType=”对象的类型”>
association 一对一或多对一
collection 一对多
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>
<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
<result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>
必须是左(外)连接,因为我主要的目的是查询用户,其次才是显示查询出来的用户的订单,如果用内连接,那么会导致没有订单的用户无法显示。
如果起了别名,那么SQL查询出来的列名就变成了别名,此时column需要赋值为别名。
SQL查询中,customer_id可以写,查出来可以不设置映射接收,没什么影响,如果不写的话,以后扩展困难。
测试类:
package com.bjpowernode.test;
import com.bjpowernode.mapper.BookMapper;
import com.bjpowernode.mapper.CustomerMapper;
import com.bjpowernode.mapper.OrdersMapper;
import com.bjpowernode.pojo.Book;
import com.bjpowernode.pojo.Customer;
import com.bjpowernode.pojo.Orders;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
CustomerMapper customerMapper;
@Before //在所有的@Test方法执行前先执行的代码
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//取出sqlSession的对象
sqlSession = factory.openSession();
customerMapper = sqlSession.getMapper(CustomerMapper.class);
}
@After
public void closeSqlSession(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void testGetCustomerById(){
Customer customer = customerMapper.getById(3);
System.out.println(customer);
}
}
针对一对多查询的改进:
这里调用本类的其他查询方法,实现代码的复用。
column标签就是找到表中指定的那一列并接收他的值,然后让其他关键字对该值进行操作。
如果collection标签是使用嵌套查询,格式如下:
<collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" >
</collection>
注意:标签中的column:要传递给select查询语句的参数,如果传递多个参数,格式为column= “{参数名1=表字段1,参数名2=表字段2} ”;
但这种方式还是有缺陷,因为我们在实际开发中,希望的是CustomerMapper.xml中全是返回Customer对象信息的SQL语句,OrdersMapper.xml中全是返回Orders对象信息的SQL语句,而这种方式就会导致一个CustomerMapper.xml文件中有返回Orders对象信息的SQL语句,不好维护。
再改进:
Collection标签的select语句有改动。
将原来本Mapper的方法调用改为了跨Mapper的方法调用,CustomerMapper.xml调用OrdersMapper.xml文件中返回Orders对象信息的SQL语句。
多对一关联关系:
订单和客户就是多对一关联,站在订单的方向查询订单的同时将客户信息查出。
数据:
同一对多。
orders实体类:
package com.bjpowernode.pojo;
/**
*
*/
public class Orders {
private Integer id;
private String orderNumber;
private Double orderPrice;
//关联下此订单的客户信息,多方持有一方的对象
private Customer customer;
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", orderNumber='" + orderNumber + '\'' +
", orderPrice=" + orderPrice +
", customer=" + customer +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public Double getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(Double orderPrice) {
this.orderPrice = orderPrice;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Orders(Integer id, String orderNumber, Double orderPrice, Customer customer) {
this.id = id;
this.orderNumber = orderNumber;
this.orderPrice = orderPrice;
this.customer = customer;
}
public Orders() {
}
}
一个客户可能对应多条订单,所以如果站在客户一方,那么就是一对多,那么客户类中需要有一个集合来装该客户的所有订单信息。
而一条订单仅仅只会对应一个客户,所以如果站在订单一方,那么就是多对一,那么订单类中需要有一个客户类的对象来装客户信息即可。(不需要集合,因为一条订单就只对应一个客户)
Customer实体类:
package com.bjpowernode.pojo;
import java.util.List;
/**
*
*/
public class Customer {
//customer表中的三个列
private Integer id;
private String name;
private Integer age;
//该客户名下的所有订单的集合,一方持有多方的集合
private List<Orders> ordersList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", ordersList=" + ordersList +
'}';
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<Orders> getOrdersList() {
return ordersList;
}
public void setOrdersList(List<Orders> ordersList) {
this.ordersList = ordersList;
}
public Customer(Integer id, String name, Integer age, List<Orders> ordersList) {
this.id = id;
this.name = name;
this.age = age;
this.ordersList = ordersList;
}
public Customer() {
}
}
OrdersMapper接口:
package com.bjpowernode.mapper;
import com.bjpowernode.pojo.Orders;
/**
*
*/
public interface OrdersMapper {
//根据主键查询订单,并同时查询下此订单的客户信息
Orders getById(Integer id);
}
OrdersMapper.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 namespace="com.bjpowernode.mapper.OrdersMapper">
<!--
//根据主键查询订单,并同时查询下此订单的客户信息
Orders getById(Integer id);
-->
<!--
手工绑定数据
实体类
private Integer id;
private String 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>
<!--多出来的一咕噜绑定
private Integer id;
private String name;
private Integer age;
//该客户名下的所有订单的集合,一方持有多方的集合
private List<Orders> ordersList; //不用管
<association property="对象名" javaType="对象的类型">
-->
<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>
内连接即可,因为不会出现没有客服的订单。
association和collection如果有需求的话是可以嵌套使用的。
测试类:
package com.bjpowernode.test;
import com.bjpowernode.mapper.BookMapper;
import com.bjpowernode.mapper.CustomerMapper;
import com.bjpowernode.mapper.OrdersMapper;
import com.bjpowernode.pojo.Book;
import com.bjpowernode.pojo.Customer;
import com.bjpowernode.pojo.Orders;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
OrdersMapper ordersMapper;
@Before //在所有的@Test方法执行前先执行的代码
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//取出sqlSession的对象
sqlSession = factory.openSession();
ordersMapper = sqlSession.getMapper(OrdersMapper.class);
}
@After
public void closeSqlSession(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void testGetOrdersById(){
Orders orders = ordersMapper.getById(11);
System.out.println(orders);
}
}
针对多对一查询的改进:
第一次改进:
缺点同一对多。
第二次改进:
一对一关联:
数据:
USE mybatis;
-- ----------------------------
-- Table structure for `tb_head_teacher`
-- ----------------------------
#DROP TABLE IF EXISTS `teacher`;
create table teacher(
tid int primary key auto_increment,
tname varchar(20),
tage int
)DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of teacher
-- ----------------------------
insert into teacher(tname,tage) values('ZhangSan',40);
#DROP TABLE IF EXISTS `classes`;
create table classes(
cid int primary key auto_increment,
cname varchar(20),
ctid int unique,
foreign key(ctid) references teacher(tid)
)DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orders
-- ----------------------------
insert into classes(cname,ctid) values('Class One',1);
select * from teacher;
select * from classes;
Teacher实体类:
package com.bjpowernode.pojo;
public class Teacher {
private Integer id;
private String name;
private Integer age;
public Teacher() {
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Teacher(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
}
Classes实体类:
package com.bjpowernode.pojo;
public class Classes {
private Integer id;
private String name;
private Teacher teacher;
@Override
public String toString() {
return "Classes{" +
"id=" + id +
", name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
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 Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public Classes(Integer id, String name, Teacher teacher) {
this.id = id;
this.name = name;
this.teacher = teacher;
}
public Classes() {
}
}
CATMapper接口:
package com.bjpowernode.mapper;
import com.bjpowernode.pojo.Classes;
/**
*
*/
public interface CATMapper {
Classes getCLassAndTeacher(Integer id);
}
CATMapper.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 namespace="com.bjpowernode.mapper.CATMapper">
<!-- Classes getCLassAndTeacher(Integer id);
Classes实体类
private Integer id;
private String name;
private Teacher teacher;
Teacher实体类:
private Integer id;
private String name;
private Integer age;
-->
<resultMap id="ClassesMap" type="Classes">
<id property="id" column="cid"></id>
<result property="name" column="cname"></result>
<association property="teacher" javaType="teacher">
<id property="id" column="tid"></id>
<result property="name" column="tname"></result>
<result property="age" column="tage"></result>
</association>
</resultMap>
<select id="getCLassAndTeacher" parameterType="int" resultMap="ClassesMap">
select c.cid,c.cname,c.ctid,t.tid,t.tname,t.tage
from classes c inner join teacher t on c.ctid=t.tid
where c.cid=1
</select>
</mapper>
测试类:
package com.bjpowernode.test;
import com.bjpowernode.mapper.BookMapper;
import com.bjpowernode.mapper.CustomerMapper;
import com.bjpowernode.mapper.OrdersMapper;
import com.bjpowernode.mapper.CATMapper;
import com.bjpowernode.pojo.Book;
import com.bjpowernode.pojo.Classes;
import com.bjpowernode.pojo.Customer;
import com.bjpowernode.pojo.Orders;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
CATMapper catMapper;
@Before //在所有的@Test方法执行前先执行的代码
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//取出sqlSession的对象
sqlSession = factory.openSession();
catMapper= sqlSession.getMapper(CATMapper.class);
}
@After
public void closeSqlSession(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void testTeacherAndClass(){
Classes classes = catMapper.getCLassAndTeacher(1);
System.out.println(classes);
}
}
多对多关联:
多对多关联中,需要通过中间表化解关联关系。中间表描述两张主键表的关联。中间表没有对应的实体类。Mapper.xml文件中也没有中间表的对应标签描述,只是在查询语句中使用中间表来进行关联。
如:老师与所教的班级就是一个多对多的关系,一个老师教多个班,一个班又有多个老师。
创建t_classes
创建t_classessTeacher
创建t_teacher
数据:
/*如果DB不存在,不会报错*/
#DROP DATABASE IF EXISTS many;
#SET FOREIGN_KEY_CHECKS=0;
CREATE DATABASE many DEFAULT CHARSET utf8;
use many;
drop table if exists t_classes;
drop table if exists t_classessTeacher;
drop table if exists t_teacher;
create table t_classes
(
cid int primary key auto_increment,
cname varchar(20)
);
create table t_teacher
(
tid int primary key auto_increment,
tname varchar(20)
);
create table t_classessTeacher
(
cid int,
tid int,
constraint fk_bid foreign key(cid) references t_classes(cid),
constraint fk_cid foreign key(tid) references t_teacher(tid)
);
insert into t_teacher values (default,'王老师');
insert into t_teacher values (default,'李老师');
insert into t_teacher values (default,'张老师');
insert into t_classes values (default,'大一');
insert into t_classes values (default,'大二');
insert into t_classes values (default,'大三');
insert into t_classessTeacher values (1,1);
insert into t_classessTeacher values (2,1);
insert into t_classessTeacher values (1,3);
insert into t_classessTeacher values (2,3);
select * from t_classes;
select * from t_teacher;
select * from t_classessTeacher;
Classes实体类:
package com.bjpowernode.pojo;
import java.util.List;
public class Classes {
private Integer cid;
private String cname;
private List<Teacher> teachers;
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public List<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(List<Teacher> teachers) {
this.teachers = teachers;
}
@Override
public String toString() {
return "Classes [cid=" + cid + ", cname=" + cname + ", teachers=" + teachers + "]";
}
}
Teacher实体类:
package com.bjpowernode.pojo;
import java.util.List;
public class Teacher {
private Integer tid;
private String tname;
private List<Classes> classes;
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public List<Classes> getClasses() {
return classes;
}
public void setClasses(List<Classes> classes) {
this.classes = classes;
}
@Override
public String toString() {
return "Teacher [tid=" + tid + ", tname=" + tname + "]";
}
}
ClassesMapper接口:
package com.bjpowernode.mapper;
import com.bjpowernode.pojo.Classes;
/**
*
*/
public interface ClassesMapper {
//根据客户的id查询客户所有信息并同时查询该客户名下的所有订单
Classes findClassesWithTeacher(Integer id);
}
ClassesMapper.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 namespace="com.bjpowernode.mapper.ClassesMapper">
<!-- 自定义结果映射 -->
<resultMap id="ClassesWithTeacherResult" type="Classes">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!-- 多表关联映射 -->
<collection property="teachers" ofType="Teacher">
<id property="tid" column="tid"/>
<result property="tname" column="tname"/>
</collection>
</resultMap>
<select id="findClassesWithTeacher" parameterType="Integer" resultMap="ClassesWithTeacherResult">
select c.cid,c.cname,t.tid,t.tname,ct.cid,ct.tid from t_classes c
inner join t_classessTeacher ct on ct.cid=c.cid
inner join t_teacher t on ct.tid=t.tid
where c.cid=#{cid}
</select>
<!--
SQL92语法:
select * from t_classes c,t_teacher t,t_classessTeacher ct
where ct.cid=c.cid
and ct.tid=t.tid
and c.cid=#{cid}
-->
</mapper>
测试类:
package com.bjpowernode.test;
import com.bjpowernode.mapper.*;
import com.bjpowernode.pojo.Classes;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
*
*/
public class MyTest {
SqlSession sqlSession;
ClassesMapper classesMapper;
@Before //在所有的@Test方法执行前先执行的代码
public void openSqlSession() throws IOException {
//使用文件流读取核心配置文件SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//取出sqlSession的对象
sqlSession = factory.openSession();
classesMapper = sqlSession.getMapper(ClassesMapper.class);
}
@After
public void closeSqlSession(){
//关闭sqlSession
sqlSession.close();
}
@Test
public void testfindClassesWithTeacher(){
Classes classes = classesMapper.findClassesWithTeacher(1);
System.out.println(classes);
}
}
事务:
事务有四个特性:一致性,持久性,原子性,隔离性。
在MyBatis框架中设置事务:
<transactionManager type="JDBC"></transactionManager> ===>程序员自己控制处理的提交和回滚
也可另外设置为自动提交:
sqlSession = factory.openSession(); ===>默认是手工提交事务,设置为false也是手工提交事务,如果设置为true,则为自动提交.
sqlSession = factory.openSession(true); ===>设置为自动提交,在增删改后不需要commit();
缓存:
MyBatis框架提供两级缓存,一级缓存和二级缓存。默认开启一级缓存。缓存就是为了提交查询效率.
使用缓存后,查询的流程:
查询时先到缓存里查,如果没有则查询数据库,放缓存一份,再返回客户端。下次再查询的时候直接从缓存返回,不再访问数据库。如果数据库中发生commit()操作,则清空缓存。(缓存存在内存中,数据库存在硬盘中,对内存的读取操作要远远快于对硬盘的读取操作)
一级缓存使用的是SqlSession的作用域,同一个sqlSession共享一级缓存的数据。
二级缓存使用的是mapper的作用域,不同的sqlSession只要访问的同一个mapper.xml文件,则共享二级缓存作用域。
代码验证一级缓存:
@Test
public void testCache() {
//第一次取id=5的用户
Users u1 = uMapper.getById(5);
System.out.println("第一次取出的用户u1:"+u1);
System.out.println("***********************************");
Users u2 = uMapper.getById(5);
System.out.println("第二次取出的用户u2:"+u2);
System.out.println(u1 == u2);
}
如果数据库中发生commit()操作,则清空缓存:
@Test
public void testCache() throws ParseException {
//第一次取id=5的用户
Users u1 = uMapper.getById(5);
System.out.println("第一次取出的用户u1:"+u1);
System.out.println("***********************************");
//执行更新操作
Users u = new Users(27,"djdj",sf.parse("2009-01-01"),"2","北京大兴亦庄djdj");
int num = uMapper.update(u);
sqlSession.commit();
Users u2 = uMapper.getById(5);
System.out.println("第一次取出的用户u1:"+u2);
System.out.println(u1 == u2);
}
ORM:
ORM(Object Relational Mapping):对象关系映射。
MyBatis框架是ORM非常优秀的框架。
java语言中以对象的方式操作数据,存到数据库中是以表的方式进行存储,对象中的成员变量与表中的列之间的数据互换的对应关系称为映射。整个这套操作就是ORM。
持久化的操作:将对象保存到关系型数据库中 ,或是将关系型数据库中的数据读取出来以对象的形式封装,两种过程都叫做数据的持久化。
MyBatis是持久化层优秀的框架。