IoC是一个容器,它会认为一切Java资源都是Java Bean,容器的目标是管理这些Bean和它们之间的关系。
所有IoC中装载了各种Bean, 也可理解为Java的各种资源,包括Java Bean的创建、事件、行为等,都由IoC管理。
Java Bean间可能存在依赖关系,IoC也能够进行管理。
Example:
用户可以使用两种插座(这两种插座都实现了同一个接口Socket)
通过代码实现使用Socket1
Socket socket = new Socket1();
user.setSocket(socket);
user.useSocket();
使用Socket socket = new Socket1();,后Socket就和Socket1捆绑在一起了。
这样会有一个弊端,如果需要使用其他插座,就需要修改代码了。
这种情况Socket接口和其实现类Socket1耦合了。
Socket socket = new Socket2();
user.setSocket(socket);
user.useSocket();
Spring IoC可以解决这个问题,我们不使用new的方式创建对象,而是让Spring IoC容器自己通过配置找到插座。
如果要使用Socket2,只需要把配置改为:
你不需要去找资源,只要向IoC容器描述所需资源,Spring IoC自己会找到你所需要的资源。
IoC根据描述找到使用者需要的资源,控制权在Spring IoC容器中,这就是控制反转的含义。
AOP常用于数据库事务的编程,在完成第一步数据库数据更新后,不知道下一步是否会成功,如果下一步失败,会使用数据库事务的回滚功能去回滚事务,使得下一步的数据库更新也作废。
在AOP实现的数据库事务管理中,是以异常作为消息的。在默认状态下,只要Spring收到了异常消息,它就会将数据库的事务回滚,从而保证数据的一致性。
我们只需要关注业务代码,知道只要发生了异常,Spring会回滚事务就行了。
MyBatis的优势在于灵活,它几乎可以代替JDBC ,同时提供了接口编程。目前MyBatis的数据访问层DAO ( Data Access Objects)是不需要实现类的,它只需要一个接口和XML(或者注解)。
我们把POJO 对象和数据库表相互映射的框架称为对象关系映射( Object Relational Mapping)框架。无论MyBatis或者Hibernate 都可以称为ORM 框架,只是Hibernate 的设计理念是完全面向POJO 的,而MyBatis 则不是。
Hibernate基本不再需要编写SQL 就可以通过映射关系来操作数据库,是一种全表映射的体现:而MyBatis 则不同,它需要我们提供SQL 去运行。
Hibernate对POJO和表进行了全表映射。然后对POJO操作,从而对t_role表里的数据进行增删改查。代码里没有SQL,因为Hibernate会根据映射关系来生成对应的SQL,只需要操作POJO就能操作对应数据库中的表了。
与Hibernate不同,MyBatis不屏蔽SQL,程序员可以自己制定SQL规则,这样能更加精确地定义SQL。
resultMap元素用于定义映射规则,Mybatis在满足一定规则下,完成自动映射,增删改查对应四个元素。
resultMap中type为返回类型,可以为JavaBean对象或者是map对象。
< id/>和< result/>是resultMap中最基本的映射内容,它们都可以将查询结果中一个单独列的值映射为返回结果对象中的简单数据类型(字符串,整型,双精度浮点数,日期等)的单独属性或字段。这两者之间的唯一不同是id 表示的结果将是当比较对象实例时用到的标识属性。
mapper元素中的namespace属性,需要和一个接口的全限定类名一致,而里面SQL的id也需要和接口定义的方法完全保持一致。
映射文件(接口):
public ReflectServiceImpl getInstance() {
ReflectServiceImpl object = null;
try {
object = (ReflectServiceImpl) Class.forName("com.lean.ssm.chapter2.reflect.ReflectServiceImpl")
.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
ex.printStackTrace();
}
return object;
}
public ReflectServiceImpl2 getInstance() {
ReflectServiceImpl2 object = null;
try {
object = (ReflectServiceImpl2) Class.forName("com.lean.ssm.chapter2.reflect.ReflectServiceImpl2").
getConstructor(String.class).newInstance("张三");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
ex.printStackTrace();
}
return object;
}
反射的优点是只要配置就可以生成对象, 可以解除程序的稿合度,比较灵活。反射的缺点是运行比较慢。但是大部分情况下为了灵活度, 降低程序的稿合度,我们还是会使用反射的,比如Spring IoC 容器。
public Object reflectMethod() {
Object returnObj = null;
ReflectServiceImpl target = new ReflectServiceImpl();
try {
Method method = ReflectServiceImpl.class.getMethod("sayHello", String.class);
returnObj = method.invoke(target, "张三");
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
ex.printStackTrace();
}
return returnObj;
}
public Object reflect() {
ReflectServiceImpl object = null;
try {
object = (ReflectServiceImpl) Class.forName("com.lean.ssm.chapter2.reflect.ReflectServiceImpl")
.newInstance();
Method method = object.getClass().getMethod("sayHello", String.class);
method.invoke(object, "张三");
} catch (NoSuchMethodException | SecurityException | ClassNotFoundException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | InstantiationException ex) {
ex.printStackTrace();
}
return object;
}
动态代理的意义在于生成一个占位(又称代理对象) , 来代理真实对象, 从而控制真实对象的访问。
代理的作用就是,在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制是否使用真实对象。
我们需要在调用者调用对象之前产生一个代理对象,而这个代理对象需要和真实对象建立代理关系,所以代理必须分为两个步骤:
JDK动态代理必须借助接口才能产生代理对象,所以定义一下接口:
public interface HelloWorld {
public void sayHelloWorld();
}
并提供实现类:
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHelloWorld() {
System.out.println("Hello World");
}
}
动态代理绑定和代理逻辑实现:
/**
* 在JDK 动态代理中,要实现代理逻辑类必须去实现java.lang.reflect.InvocationHandler
* 接口,它里面定义了一个invoke 方法,并提供接口数组用于下挂代理对象
*/
public class JDKProxy implements InvocationHandler {
//真实对象
private Object target = null;
/**
* 建立代理对象和真实对象的关系,并返回代理对象
* @param target
* @return 代理对象
*/
public Object bind(Object target){
//用类属性保存真实对象
this.target = target;
//建立并生成代理对象
//三个参数
//1.真实对象的类加载器
//2.把生成的动态代理对象挂在哪些接口下 -> 真实对象实现的接口
//3.定义实现方法逻辑的代理类 -> this表示当前对象 它实现的invoke方法就是代理逻辑方法的实现
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 代理方法逻辑
* @param proxy 代理对象 即bind方法生成的对象
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法");
System.out.println("在调度真实对象之前的服务");
//相当于调度真实对象的方法,只是通过反射实现
Object obj = method.invoke(target, args);
System.out.println("在调度真实对象之后的服务");
return obj;
}
}
测试代码:
public class Test {
public static void main(String[] args) {
JDKProxy jdk = new JDKProxy();
//绑定关系,因为挂在HelloWorld接口下,所以声明HelloWorld proxy
//调用bind方法,参数传入真实对象HelloWorldImpl
//此时proxy对象已经是一个代理对象,在调用方法时,它会进入代理方法逻辑invoke里
HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
//代理对象调用sayHelloWorld方法时进入了代理的逻辑
proxy.sayHelloWorld();
/* 输出结果:
*
进入代理逻辑方法
在调度真实对象之前的服务
Hello World
在调度真实对象之后的服务
*
* */
}
}
由于动态代理一般都比较难理解,程序设计者会设计一个拦截器接口供开发者使用,开发者只要知道拦截器接口的方法、含义和作用即可,无须知道动态代理是怎么实现的。
用JDK动态代理来实现一个拦截器的逻辑,为此先定义拦截器接口Interceptor。
/**
* proxy:代理对象
* target:真实对象
* method:方法
* args:方法参数
*
* before在真实对象前调用,当返回true,则反射真实对象的方法;当返回false,则调用around方法。
* 在反射真实对象方法或调用around方法后,调用after方法。
*/
public interface Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args);
public void around(Object proxy, Object target, Method method, Object[] args);
public void after(Object proxy, Object target, Method method, Object[] args);
}
实现接口:
public class MyInterceptor implements Interceptor{
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法前逻辑");
return false;//不反射被代理对象原有方法,调用around
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("取代了被代理对象的方法,即真实对象的方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法后逻辑");
}
}
在JDK动态代理中使用拦截器:
public class InterceptorJdkProxy implements InvocationHandler {
private Object target; //真实对象
private String interceptorClass = null; //拦截器全限定名
public InterceptorJdkProxy(Object target,String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
*
* @param target 真实对象
* @param interceptorClass 拦截器全限定类名
* @return 代理对象【占位】
*/
public static Object bind(Object target, String interceptorClass){
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InterceptorJdkProxy(target,interceptorClass));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(interceptorClass == null){
//没有设置拦截器则直接反射原有方法
return method.invoke(target,args);
}
Object result = null;
//通过反射生成拦截器
Interceptor interceptor = (Interceptor)
Class.forName(interceptorClass).newInstance();
//调用前置方法
if(interceptor.before(proxy,target,method,args)){
//反射原有对象方法
result = method.invoke(target,args);
}else {
//返回false执行around方法
interceptor.around(proxy,target,method,args);
}
//调用后置方法
interceptor.after(proxy,target,method,args);
return result;
}
}
测试类:
public class Test {
public static void main(String[] args) {
HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),
"chapter2.intercept.MyInterceptor");
proxy.sayHelloWorld();
}
}
/* 执行结果:
反射方法前逻辑
取代了被代理对象的方法,即真实对象的方法
反射方法后逻辑*/
当一个对象在一条链上被多个拦截器拦截处理(拦截器也可以选择不拦截处理它)时,我们把这样的设计模式称为责任链模式,它用于一个对象在多个角色中传递的场景。
定义三个拦截器
public class Interceptor1 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("拦截器1的before");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("拦截器1的after");
}
}
public class Interceptor2 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("拦截器2的before");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("拦截器2的after");
}
}
public class Interceptor3 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("拦截器3的before");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("拦截器3的after");
}
}
测试加入责任链逻辑拦截器的代理对象
public static void main(String[] args) {
HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),"chapter2.intercept.Interceptor1");
HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(proxy1,"chapter2.intercept.Interceptor2");
HelloWorld proxy3 = (HelloWorld) InterceptorJdkProxy.bind(proxy2,"chapter2.intercept.Interceptor3");
proxy3.sayHelloWorld();
/* 拦截器3的before
拦截器2的before
拦截器1的before
Hello World
拦截器1的after
拦截器2的after
拦截器3的after*/
}
观察者模式又称为发布订阅模式,是对象的行为模式。观察者模式定义了一种一对多
的依赖关系,让多个观察者对象同时监视着被观察者的状态,当被观察者的状态发生变化
时, 会通知所有观察者, 并让其自动更新自己。
public class ProductList extends Observable {
private List productList = null; //产品列表
private static ProductList instance; //类唯一实例
private ProductList(){} //构造方法私有化
//构建方法私有化,避免通过n ew 的方式创建对象, 而是通过getInstance方法获得产
品列表单例,这里使用的是单例模式。
/**
* 取得唯一实例
* @return
*/
public static ProductList getInstance(){
if(instance == null){
instance = new ProductList();
instance.productList = new ArrayList();
}
return instance;
}
/**
* 增加观察者(电商接口)
* @param observer
*/
public void addProductObserver(Observer observer){
this.addObserver(observer);
}
/**
* 核心逻辑在addProduct 方法上。在产品列表上增加了一个新的产品,然后调用
* setChanged 方法。这个方法用于告知观察者当前被观察者发生了变化,如果没有,
* 则无法触发其行为。最后通过notifyObservers 告知观察者,让它们发生相应的动作,
* 并将新产品作为参数传递给观察者。
* @param newProduct
*/
public void addProduct(String newProduct){
productList.add(newProduct);
System.out.println("产品列表新增产品:"+newProduct);
this.setChanged();//设置被观察对象发生变化
this.notifyObservers(newProduct);//通知观察者,并传递新产品
}
}
public class TaoBaoOb implements Observer {
@Override
public void update(Observable o, Object product) {
String newProdcut = (String) product;
System.out.println("发送新产品:"+newProdcut+"到淘宝");
}
}
public class JingDongOb implements Observer {
@Override
public void update(Observable o, Object product) {
String newProdcut = (String) product;
System.out.println("发送新产品:"+newProdcut+"到京东");
}
}
测试代码:
public static void main(String[] args) {
ProductList observable = ProductList.getInstance();
TaoBaoOb tb = new TaoBaoOb();
JingDongOb jd = new JingDongOb();
observable.addObserver(tb);
observable.addObserver(jd);
observable.addProduct("新产品XXX");
/*输出:
* 产品列表新增产品:新产品XXX
发送新产品:新产品XXX到京东
发送新产品:新产品XXX到淘宝*/
}
在大部分的情况下,我们都是以new 关键字来创建对象的。举个例子,现实中车子的
种类可能很多,有大巴车、轿车、救护车、越野车、卡车等,每个种类下面还有具体的型
号,一个工广生产如此多的车会难以管理,所以往往还需要进一步拆分为各个分工厂: 大
巴车、轿车等分工厂。但是客户不需要知道工厂是如何拆分的,他只会告诉客服需要什么
车,客服会根据客户的需要找到对应的工厂去生产车。对客户而言,车厂只是一个抽象概
念,他只是大概知道有这样的一个工厂能满足他的需要。
例如,有个Product的产品接口,它下面有5 个实现类
Product1、Product2 、Pro duct3 、Product4 、Products 。它们属于一个大类,可以通过一个工厂去管理它们的生成,但是由于类型不同,所以初始化有所不同。为了方便使用产品工厂
( ProductFactory )类来创建这些产品的对象,用户可以通过产品号来确定需要哪种产品。
伪代码:
public class ProductFactory{
public static Product createProduct(String productNo) {
switch (productNo) {
case ” 1 ”: return new Productl(xxxx);
case ” 2 ”: return new Product2(xxxx);
case ” 3 ”: return new Product3(xxxx);
case ” 4 ”: return new Product4(xxxx);
case ” 5 ”: return new Product5(xxxx);
default : throw new
NotSupportedException ( ” 未支持此编号产品生产。”);
}
}
}
对于程序调用者而言,它只需要知道通过工厂的createProduct 方法,指定产品编号——productNo 可以得到对应的产品,而产品满足接口Product 的规范,所以初始化就简单了许
多。对于产品对象的创建,可以把一些特有产品规则写入工厂类中。
对于普通工厂而言,它解决了一类对象创建问题,但是有时候对象很复杂,有几十种,
又分为几个类别。如果只有一个工厂,面对如此多的产品,这个工厂需要实现的逻辑就太
复杂了,所以我们希望把工厂分成好几个,这样便于工厂产品规则的维护。
但是设计者并不想让调用者知道具体的工厂规则,而只是希望他们知道有一个统一的工厂即可。这样的设计有助于对外封装和简化调用者的使用,毕竟调用者可不想知道选择具体工厂的规则。
以车厂为例,生产商不会把轿车、大巴车、警车、吉普车、救护车等车型都放在一
个车厂生产, 那样会造成车厂异常复杂, 从而导致难以管理和维护。所以, 生产商通常会
把它们按种类分为轿车厂、大巴车厂、警车厂、吉普车厂等分厂,每个种类下面有一些型
号的产品。但是对于客户而言, 只要告诉客服自己需要什么类型的车即可, 至于如何分配
给工厂那是客服的事情。
客户只是认为有一个能够生产各类车的工厂, 它能生成我所需要的产品, 这里工厂只
是一个虚拟的概念, 并不真实存在,它是通过车厂内部各个分厂去实现的, 这个虚拟工厂
被称为抽象工厂, 它的各个分厂称为具体工厂。为了统一, 需要制定一个接口规范
, 所有的具体工厂和抽象工厂都要实现这个接口。
建造者模式属于对象的创建模式。可以将一个产品的内部表象(属性〉与产品的生成
过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。
构建套票对象:
TicketHelper helper= new TicketHelper() ;
helper . buildAdult (” 成人票”) ;
helper . buildChildrenForSeat ( ” 有座儿童” ) ;
helper . buildchildrenNoSeat ( ” 无座儿童” ) ;
helper . buildElderly (” 老人票” );
helper . buildSoldier(” 军人票”) ;
Object ticket = TicketBuilder.builder(helper) ;
构建分成若干步,通过一步步构建信息,把一个复杂的对象构建出来。
持久层可以将业务数据存储到磁盘,具备长期存储能力,只要磁盘不损坏(大部分的
重要数据都会有相关的备份机制),在断电或者其他情况下,重新开启系统仍然可以读取这
些数据。
配置事务管理方式- JDBC
配置数据库 POOLED代表使用Mybatis内部提供的连接池方式
创建SqlSessionFactory
SqlSessionFactory SqlSessionFactory = null;
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e){
e.printStackTrace();
}
创建方法:
SqlSession sqlSession = SqlSessionFactory.openSession();
映射器是MyBatis中最重要、最复杂的组件。
由一个接口和对应的XML文件(或注解)组成。
它配置以下内容:
在测试之前,首先定义一个POJO:
public class Role {
private Long id;
private String roleName;
private String note;
/** setter and getter **/
...
}
创建映射器接口:
public interface RoleMapper {
public Role getRole(Long id);
}
在mybatis-config.xml(即创建SqlSession的配置文件)中配置:
它的作用是引入一个XML文件。用XML方式创建映射器:
有了这两个文件(映射器接口和RoleMapper.xml),就完成了一个映射器的定义。
有了映射器就可以通过Sq!Session 发送SQL 了。我们以getRole 这条SQL 为例看看如何发送SQL 。
Role role = (Role)sqlSession.selectOne("com.learn.ssm.chapter3.mapper.RoleMapper.getRole",1L);
selectOne方法代表使用查询并且只返回一个对象,而参数则是一个String对象和一个Object对象。
1L:这里是一个long 参数, long 参数是它的主键。
“com.learn.ssm.chapter3.mapper.RoleMapper.getRole”:String对象是由一个命名空间加上SQLid组合而成的,它完全定位了一条SQL,这样MyBatis就会找到对应的S QL。
SqlSession还可以获取Mapper接口,通过Mapper接口发送SQL
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1L);
通过sqlSession的getMapper方法获取一个Mapper接口就可以调用这个接口的方法了。
这里调用了RoleMapper接口的getRole()方法。
作用在于创建SqlSessionFactory ,创建成功后,就失去了作用,所以它只能存在于创建Sq!SessionFactory的方法中,而不要让其长期存在。
Sq!SessionFactory 可以被认为是一个数据库连接池,它的作用是创建SqlSession 接口对象。因为MyBatis 的本质就是Java 对数据库的操作,所以SqlSessionFactory 的生命周期存在于整个My Batis 的应用之中,所以一旦创建了SqlSessionFactory , 就要长期保存它,直至不再使用MyBatis 应用,所以可以认为SqlSessionFactory 的生命周期就等同于MyBatis 的应用周期。
由于SqlSe ssionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个SqlSessionFactory ,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统密机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望Sq!SessionFactory 作为一个单例,让它在应用中被共享。
Mapper 是一个接口,它由SqlSession 所创建,所以它的最大生命周期至多和SqlSe ssion保持一致,尽管它很好用,但是由于SqlSession 的关闭,它的数据库连接资源也会消失,所以它的生命周期应该小于等于SqlSession 的生命周期。Mapper 代表的是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。
<!一配置一>
< ! 一属性一>
< ! 一设置一>
<! 一类型命名一>
< !一类型处理器一>
< !一对象工厂一>
<!一插件一>
<! 一配置环境一>
< ! 一环境变量一>
< ! 一事务管理器一>
< !一数据源一>
< !一数据库厂商标识一〉
< ! 一映射器一〉
给系统配置运行参数
大部分情况下使用默认值, 详细内容可以查表。
定义别名来代表全限定类名。
在Mybatis的初始化过程中,系统自动初始化了一些别名:
在mybatis-config.xml配置文件中定义:
此外,MyBatis还支持扫描别名
这样MyBatis会扫描这个包里的类,并将其首字母小写作为其别名,即Role类的别名是role.
在JDBC 中, 需要在PreparedStatement 对象中设置那些已经预编译过的SQL 语句的参数。执行SQL 后,会通过ResultSet 对象获取得到数据库的数据,而这些,MyBatis 是根据数据的类型通过ψpeHandler 来实现的。
typeHandler的作用是承担jdbcType和javaType之间的相互转换。
大多数情况下,我们不需要配置typeHandler,对于特殊场景,可以自定义typeHandler.
当创建结果集时, MyBatis 会使用一个对象工厂来完成创建这个结果集实例。在默认
的情况下, MyBatis 会使用其定义的对象工厂一DefaultObjectFactory来完成对应的工作。
作用是配置数据库信息,数据库信息分为事务管理器和数据源。
主要是支持多种不同厂商的数据库, 并不常用。
public interface RoleMapper{
public Role getRole(Long id);
}
....
在实际工作中,常用的是id, parameterType, resultType, resultMap, 如果要设置缓存,还会使用到flushCache, useCache.
此外,我们还需要定义接口方法才能使程序运行:
public Integer countUserByFirstName(String firstName);
自动映射默认开启,在setting元素中可以配置自动映射和驼峰转换的开关。
为了实现自动映射,首选要有POJO:
public class Role {
private Long id;
private String roleName;
private String note;
/**setter and getter**/
....
}
如果编写的SQL列名和属性名保持一致,那么它就会形成自动映射。
原来的列名role_name被roleName代替了,这样就和POJO的属性名一致了,形成了自动映射。
如果系统都严格按照驼峰命名法,如数据库字段为role_name, 则POJO属性名为roleName; 又如数据库字段为user_name, 则POJO属性名为userName, 那么只要在配置项把mapUnderscoreToCamelCase设置为true即可。如果这样做,那么上面的SQL可以改写成:
select id, role_name, note from t_role where id = #{id}
假设要通过角色名称(role_name)和备注(note)对角色进行模糊查询,这样就需要两个参数了。
不推荐,省略。
public List findRolesByAnnotation(@Param("roleName") String rolename, @Param("note") String note);
此时不要再给出parameterType属性,让MyBatis自动探索便可以了。
先定义一个参数的POJO:
public class RoleParams {
private String roleName;
private String note;
/**setter and getter**/
}
此时接口方法定义为:
public List findRolesByBean(RoleParams roleParam);
mapper文件:
查询:
sqlSession = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
RoleParams roleParam = new RoleParams();
roleParam.setNote("1");
roleParam.setRoleName("1");
List roles = roleMapper.findRolesByBean(roleParam);
在某些情况下可能需要混合使用几种方法来传递参数。举个例子,查询一个角色,可
以通过角色名称和备注进行查询,与此同时还需要支持分页,而分页的POJO 实现:
public class RageParams {
private int start;
private int limit;
/**setter and getter**/
}
接口:
public List findByMix(@Param("params") RoleParams roleParams, @Param("page") PageParam PageParam)
待补充
insert into t_role(role_name,note) values (#{roleName},#{note})
上方的简单插入语句没有插入id列,因为MySQL中的表格使用了自增主键,会为这条记录自动生成主键。
有时候可能需要继续使用这个主键,用以关联其他业务,所以需要提取这个id(主键)。
比如新增用户时,肯先会插入用户表的记录,然后插入用户和角色关系表,插入用户时如果没有办法取到用户的主键,那么就没有办法插入用户和角色关系表了,因此在这个时候要拿到对应的主键。
在insert语句中有一个开关属性useGeneratedKeys,用来控制是否打开获得生成主键的功能,它的默认值是false。
当打开这个开关时,还要配置其属性keyProperty或者keyColumn,告诉系统把生成的主键放入哪个属性中,如果存在多个主键,要用逗号隔开。
insert into t_role(role_name, note) values(#{roleName},#{note})
keyProperty代表将用哪个POJO的属性去匹配这个主键,这里是id, 说明它会把数据库生成的主键值赋值给POJO的id属性。
有时候主键的规则并不是自增的,比如将主键规则修改为:
此时需要使用selectKey元素进行支持。
select if (max(id) = null , 1, max(id) + 3) from t_role
insert into t_role(id, role_name, note) values(#{id}, #{roleName}), #{note})
update t_role set role_name = #{roleName}, note = #{note}
where id = #{id}
delete from t_role where id = #{id}
完成后MyBatis会返回一个整数,标识影响了多少条数据。
可以定义一条SQL的一部分,方便后面的SQL引用它。
id,role_name,note
constructor元素用于配置构造方法,一个POJO可能不存在没有参数的构造方法,可以使用constructor进行配置。
假设角色类RoleBean不存在没有参数的构造方法,它的构造方法声明为public RoleBean(Integer id, String roleName), 那么需要配置结果集:
id元素表示哪个列是主键,允许多个主键,多个主键则称为联合主键。result 是配置
POJO 到SQL 列名的映射关系。result 元素和idArg 元素的属性:
一方面可以所使用的自动映射,正如使用resultType属性一样。
有时候更复杂的映射或者级联,可以使用select语句的resultMap属性配置映射集合。
配置resultMap
这样,POJO就和数据库SQL的结果一一对应起来了。
然后在mapper文件中的select元素中配置:
第五章待补充。。
举例:根据角色名称(roleName)去查找角色,但是角色名称是一个选填条件,不填写时,就不要它作为条件查询。
注:where 后的 1=1 是为了if语句控制的后半段不执行时语句依然正确。
与if大同小异, 类似 switchl…case…default
使用where标签以代替上面奇怪的 1=1条件:
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
trim元素也能完成相同的功能:
prefix代表语句前缀,prefixOverrides代表需要去掉哪种字符串
set元素常用语更新字段
遍历集合,往往用语SQL中的In关键词。
比如查找一个ID集合中所有ID对应的详细信息:
test的作用相当于判断真假,在大部分场景中,它都是用以判断空和非空。
与if元素配合使用。
bind 元素的作用是通过OGNL 表达式去自定义一个上下文变量,这样更方便使用。
如果是MySQL 数据库,常常用到的是一个concat,它用“%”和参数相连。然而在Oracle 数据库则没有, Oracle 数据库用连接符号“||”,这样SQL 就需要提供两种形式去实现。但是有了bind 元素,就不必使用数据库的语言,而是使用MyBatis 的动态SQL 即可完成。
使用bind进行模糊查询:
同时,Mybatis也支持多个参数使用bind元素的用法:
定义接口:
public List findRole(@Param("roleName")String roleName, @Param("note")String note);
待学习补充。。