Water's Blog

坚持更新博客中...


  • 首页

  • 标签

  • 分类

  • 归档

  • 搜索

设计模式 —— Proxy 代理模式

发表于 2019-08-20 更新于 2020-01-30 分类于 设计模式 , Proxy
本文字数: 7.2k 阅读时长 ≈ 7 分钟

简介

  • Proxy代理模式是一种结构型设计模式。通过代理模式可以实现程序之间的解耦,可以解决在直接访问对象时带来的程序之间耦合度增大的问题。
  • 按照代理的创建时期,代理类分为两种:
    • 静态代理:在编译期,手动创建代理类,而不是由编译器生成代理类。
    • 动态代理:在运行期,运用反射机制动态而生成代理类。
      • JDK代理基于接口接口
      • CGlib代理基于继承

静态代理

  • 优点
    • 静态代理可以将目标类和目标方法封装起来,有隐蔽的作用。
  • 缺点
    • 静态代理类只能为特定的接口服务。
    • 如想要为多个接口服务则需要建立很多个代理类。

目录结构

  • cn.water
    • main
      • java
        • IProducer.java(接口)
        • Producer.java(实现类)
        • ProxyProducer.java(代理类)
    • test
      • staticTest.java(测试类)

接口

IProducer.java

1
2
3
4
5
6
7
8
9
10
11
package cn.water.StaticProxy;

public interface IProducer {

/** 销售 */
void saleProduct(float money);

/** 售后 */
void afterService(float money);

}

实现类

Producer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.water.StaticProxy;

public class Producer implements IProducer {

/** 销售 */
public void saleProduct(float money) {
System.out.println("生产者:通过[销售],获得了"+money+"元");
}

/** 售后 */
public void afterService(float money) {
System.out.println("生产者:通过[售后],获得了"+money+"元");
}
}

代理类

ProxyProducer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.water.StaticProxy;

public class ProxyProducer implements IProducer {

private Producer producer = new Producer();

/** 销售 */
public void saleProduct(float money) {
System.out.println("消费者:消费了"+money+"元");
producer.saleProduct(money * 0.2f);
}

/** 售后 */
public void afterService(float money) {
System.out.println("消费者:消费了"+money+"元");
producer.afterService(2);
}
}

测试类

staticTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cn.water.test;

import cn.water.StaticProxy.IProducer;
import cn.water.StaticProxy.Producer;
import cn.water.StaticProxy.ProxyProducer;
import org.junit.Test;

public class staticTest {

@Test
public void test(){

/** 成员变量 */
final Producer producer = new Producer();
/** 获取动态代理对象 */
IProducer proxy = new ProxyProducer() ;
/** 调用方法 */
/* 销售方法经过加强,增强的方法被调用 */
proxy.saleProduct(999.12f);
/* 销售方法经过加强,方法不被调用 */
proxy.afterService(99f);

}


}

动态代理(JDK)

  • JDK动态代理基于 Java的反射机制 实现。
    • 因为Java的反射机制基于接口,所以目标类一定要有接口。
  • 基于JDK技术动态代理类技术核心:Proxy类 和 InvocationHandler接口。(java.lang.reflect)
    • Proxy类中定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class... interfaces),返回class实例代表一个class文件。生成的动态代理类继承Proxy类(重要特性) ,并实现公共接口。
    • InvocationHandler接口 是被动态代理类回调的接口,我们所有需要增加的处理逻辑都添加到 invoke方法里面。
  • 匿名内部类参数必须为final类型

目录结构

  • cn.water
    • IProducer.java(接口)
    • Producer.java(实现类)
  • test
    • staticTest.java(测试类)

接口

IProducer.java

1
2
3
4
5
6
7
8
9
10
11
package cn.water.DynamicProxy.jdk;

public interface IProducer {

/** 销售 */
void saleProduct(float money);

/** 售后 */
void afterService(float money);

}

实现类

Producer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.water.DynamicProxy.jdk;

public class Producer implements IProducer {

/** 销售 */
public void saleProduct(float money) {
System.out.println("生产者:通过[销售],获得了"+money+"元");
}

/** 售后 */
public void afterService(float money) {
System.out.println("生产者:通过[售后],获得了"+money+"元");
}
}

测试类

staticTest.java

  • Proxy.newProxyInstance方法
    • 加载器
    • 接口
    • InvocationHandler接口
      • invoke方法
        • method:代表方法
        • args:参数
  • 匿名内部类只能访问方法内修饰符为final的参数
    • 为了保持数据的一致性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package cn.water.test;

import cn.water.DynamicProxy.jdk.IProducer;
import cn.water.DynamicProxy.jdk.Producer;
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class jdkTest {

@Test
public void test(){
/** 1、成员变量 */
final Producer producer = new Producer();
/** 2、获取动态代理对象 */
IProducer proxy = (IProducer) Proxy.newProxyInstance(
/* 类加载器 */
producer.getClass().getClassLoader(),
/* 实现类的接口 */
producer.getClass().getInterfaces(),
/** 增强代码 */
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
/* 方法参数 */
Float money = (Float) args[0];
/* 方法名 */
if ("saleProduct".equals(method.getName())) {
/* 调用方法 */
System.out.println("消费者:消费了"+money+"元");
result = method.invoke(producer, money * 0.2f);
}
if ("afterService".equals(method.getName())) {
/* 调用方法 */
System.out.println("消费者:消费了"+money+"元");
result = method.invoke(producer, 5);
}
/* 返回 */
return result;
}
}
);
/** 3、调用方法 */
/* 销售方法经过加强,增强的方法被调用 */
proxy.saleProduct(999.12f);
/* 销售方法经过加强,方法不被调用 */
proxy.afterService(999.12f);
}

}

动态代理(cglib)

目录结构

  • cn.water
    • Producer.java(类)
  • test
    • staticTest.java(测试类)

实现类

Producer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.water.DynamicProxy.cglib;

import cn.water.DynamicProxy.jdk.IProducer;

public class Producer {

/** 销售 */
public void saleProduct(float money) {
System.out.println("生产者:通过[销售],获得了"+money+"元");
}

/** 售后 */
public void afterService(float money) {
System.out.println("生产者:通过[售后],获得了"+money+"元");
}
}

测试类

  • Enhancer.create方法
    • 字节码
    • MethodInterceptor接口
      • intercept方法
        • method:代表方法
        • args:参数

staticTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package cn.water.test;

import cn.water.DynamicProxy.cglib.Producer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;


public class cglibTest {

@Test
public void test() {
/** 1、成员变量 */
final Producer producer = new Producer();
/** 2、获取动态代理对象 */
Producer cglibProducer = (Producer) Enhancer.create(
/* 字节码 */
producer.getClass(),
/* CallBack子类 */
new MethodInterceptor() {
/** 增强代码 */
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
/* 方法参数 */
Float money = (Float) args[0];
/* 方法名 */
if ("saleProduct".equals(method.getName())) {
/* 调用方法 */
System.out.println("消费者:消费了"+money+"元");
result = method.invoke(producer, money * 0.2f);
}
if ("afterService".equals(method.getName())) {
/* 调用方法 */
System.out.println("消费者:消费了"+money+"元");
result = method.invoke(producer, 5);
}
/* 返回 */
return result;
}
});
/** 3、调用方法 */
/* 销售方法经过加强,增强的方法被调用 */
cglibProducer.saleProduct(999.12f);
/* 销售方法经过加强,方法不被调用 */
cglibProducer.afterService(999.12f);
}

}

Spring框架(7) —— CRUD操作案例

发表于 2019-08-19 更新于 2020-01-31 分类于 Java , Spring
本文字数: 26k 阅读时长 ≈ 23 分钟

简介

  • 本文将在 Spring 框架中,分别使用使用“XML”,“XML+注解”和“注解”三种方式来实现数据库的CRUD操作。实现数据库的CRUD操作的功能属于另外一部分的知识点,三种方式的不同点主要在于如何注入依赖。
    • 基于xml配置文件
      • 依赖注入:xml配置文件
      • 开启注解扫描:xml配置文件
    • 基于xml配置文件和注解
      • 依赖注入:注解
      • 开启注解扫描:注解、xml配置文件
    • 基于注解
      • 依赖注入:注解
      • 开启注解扫描:Java配置文件
阅读全文 »

Spring框架(6) —— 基于注解的配置方式

发表于 2019-08-17 更新于 2020-01-31 分类于 Java , Spring
本文字数: 5.9k 阅读时长 ≈ 5 分钟

简介

  • 之前我们学习了基于xml文件的配置方法,今天我们学习基于注解的配置方法。

目录结构

  • src
    • main
      • java
        • cn.water
          • POJO.java
          • TextEditor.java
          • SpellCheck.java
      • resources
        • Beans.xml
    • test
      • java
        • cn.water.test
          • SpringTest.java

开启注解扫描

  • 在使用基于注解的注入依赖之前,我们需要在 Spring 配置文件中指定需要 Spring 去自动扫描出注解配置的文件的包。一旦 被配置后,你就可以开始注解你的代码,表明 Spring 会自动连接值到属性,方法和构造函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 开启注解扫描 -->
<context:component-scan base-package="cn.water"></context:component-scan>

</beans>
阅读全文 »

Spring框架(5) —— 自动装配

发表于 2019-08-16 更新于 2020-01-31 分类于 Java , Spring
本文字数: 6.2k 阅读时长 ≈ 6 分钟

Spring Beans 自动装配

  • 我们已经学会如何使用<bean>元素来声明 bean 和通过使用 XML 配置文件中的<constructor-arg>和<property>元素来注入 。而Spring 容器还可以在不使用<constructor-arg>和<property> 元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。

自动装配模式

  • 下列自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入。你可以使用<bean>元素的 autowire 属性为一个 bean 定义指定自动装配模式。
    • no
      • 默认设置,它意味着没有自动装配,表示我们需要使用显式的bean引用来注入依赖。
    • byName
      • 由属性名自动装配。
      • Spring 先匹配POJO类的类成员名称与配置文件中<bean>标签的id属性,如果名称相同,则匹配类型;类型相同,则配对成功。
    • byType
      • 由属性数据类型自动装配。
      • Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。
    • constructor
      • 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。
    • autodetect
      • Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。
阅读全文 »

Spring框架(4) —— 依赖注入(DI)

发表于 2019-08-15 更新于 2020-01-31 分类于 Java , Spring
本文字数: 24k 阅读时长 ≈ 21 分钟

依赖注入

  • Spring框架的核心功能之一就是通过依赖注入(Dependency Injection)的方式来管理Bean之间的依赖关系。
  • 每个基于应用程序的 java 多多少少都会使用几个对象,最后与这些对象一起工作来呈现出终端用户所看到的工作的应用程序。依赖注入有助于把这些类粘合在一起,同时保持他们独立。

全面控制

  • 假设你现在正在编写一个文本编辑器(TextEditor)的应用程序,并且想要添加拼写检查(SpellCheck)的功能。那么你就需要在TextEditor类中使用SpellCheck类。
  • 标准代码看起来是这样的:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class TextEditor {

    private SpellCheck spellCheck;

    public TextEditor() {
    spellCheck = new SpellCheck();
    }

    }

控制反转

  • 而在 Spring 框架中,我们需要做的则是创建一个 TextEditor 和 spellCheck 之间的依赖关系。
  • 在控制反转的场景中,我们反而会做这样的事情:
    1
    2
    3
    4
    5
    6
    7
    8
    public class TextEditor {

    private SpellCheck spellCheck;

    public TextEditor(SpellCheck spellCheck) {
    this.spellCheck = spellCheck;
    }
    }

结论

  • 在 Spring 框架的控制下,TextEditor 不需要担心 spellCheck 的实现。spellCheck 将会独立实现,并且在 TextEditor 实例化的时候将提供给 TextEditor。
    • 我们从 TextEditor 中删除了全面控制,并且把它保存到其他地方(即 XML 配置文件),且依赖关系通过类构造函数被注入到 TextEditor 类中。因此,控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。
    • 对象不查找它的依赖关系,也不知道依赖关系的位置或类,而这一切都由 Spring 框架控制的。
阅读全文 »

Spring框架(3) —— Bean对象的配置方式

发表于 2019-08-14 更新于 2020-01-31 分类于 Java , Spring
本文字数: 12k 阅读时长 ≈ 11 分钟

简介

  • Spring Bean对象
    • Bean对象 是构成应用程序的支柱。
    • Bean对象 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。
    • Bean对象 是由用 Spring IoC 容器提供的配置元数据创建的。
      • 把配置元数据提供给 Spring IoC 容器的方法:
        1. 基于 XML 的配置文件
        2. 基于注解的配置
        3. 基于 Java 的配置

属性

构成每个 Bean对象 定义的一组属性:

属性 描述
class 强制属性,用来指定创建 bean 的类。
name / id 唯一的 bean 标识符。
scope 指定 bean 对象的作用域
constructor-arg 注入依赖关系(无参构造)
properties 注入依赖关系
destroy-method 初始化方法
init-method 销毁方法

目录结构

  • src
    • main
      • java.cn.water
        • POJO.java(实体类)
        • POJOFactory.java(工厂类)
      • resources
        • LifeCycle
          • Beans.xml(Spring配置文件)
        • NewInstance
          • Beans.xml(Spring配置文件)
        • Scope
          • Beans.xml(Spring配置文件)
    • test
      • java.cn.water.test
        • LifeCycle
          • SpringTest.java(测试类)
        • NewInstance
          • SpringTest.java(测试类)
        • Scope
          • SpringTest.java(测试类)
  • pom.xml(Maven配置文件)

Maven配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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>cn.water</groupId>
<artifactId>section02_Bean</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<!-- Spring框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>

实体类

POJO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package cn.water;

/**
* @author Water
* @date 2019/10/22 - 17:26
* @description 实体类
*/
public class POJO {

/* 成员变量 */
private String message;


/* 成员方法:初始化 */
public void init(){
System.out.println("POJO类:初始化成功!");
}

/* 成员方法:销毁 */
public void destroy(){
System.out.println("POJO类:销毁成功!");
}

/* 构造方法 */

public POJO() {
System.out.println("POJO类:实例化成功!");
}
}

工厂类

POJOFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.water;

/**
* @author Water
* @date 2019/10/23 - 10:36
* @description 工厂类
*/
public class POJOFactory {

/* 成员方法 */
public POJO getPOJO() {
return new POJO();
}

/* 静态方法 */
public static POJO havePOJO(){
return new POJO();
}

}

配置文件

LifeCycle

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">


<bean id="pojo" class="cn.water.POJO" init-method="init" destroy-method="destroy"></bean>

</beans>

NewInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">


<!-- 无参构造 -->
<bean id="pojo01" class="cn.water.POJO"></bean>

<!-- 工厂类 成员方法 -->
<bean id="factory" class="cn.water.POJOFactory"></bean>
<bean id="pojo02" class="cn.water.POJO" factory-bean="factory" factory-method="getPOJO"></bean>

<!-- 工厂类 静态方法 -->
<bean id="pojo03" class="cn.water.POJOFactory" factory-method="havePOJO"></bean>


</beans>

Scope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<!-- 单例 -->
<bean id="pojo01" class="cn.water.POJO" scope="singleton"></bean>

<!-- 多例 -->
<bean id="pojo02" class="cn.water.POJO" scope="prototype"></bean>

</beans>

测试类

LifeCycle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package cn.water.test.LifeCycle;

import cn.water.POJO;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.beans.Beans;

/**
* @author Water
* @date 2019/10/23 - 10:10
* @description 测试Spring框架 Bean对象的生命周期
*/
public class SpringTest {


@Test
public void test01(){
/* 1、加载配置文件,初始化Bean对象 */
AbstractApplicationContext abstractApp = new ClassPathXmlApplicationContext("LifeCycle/Beans.xml");
/* -- init-method 方法执行 --- */
/* 2、获取Bean对象 */
POJO pojo = abstractApp.getBean("pojo", POJO.class);
/* 3、注销容器 */
abstractApp.registerShutdownHook();
/* -- destroy-method 方法执行 --- */
}

}

NewInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.water.test.NewInstance;

import cn.water.POJO;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @author Water
* @date 2019/10/23 - 10:10
* @description 测试Spring框架 Bean对象的实例化
*/
public class SpringTest {


@Test
public void test01(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("NewInstance/Beans.xml");
}

}

Scope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package cn.water.test.Scope;

import cn.water.POJO;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

/**
* @author Water
* @date 2019/10/23 - 8:39
* @description 测试Spring框架 Bean对象的作用域
*/
public class SpringTest {

/** 单例模式(默认) */
@Test
public void test01(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("Scope/Beans.xml");
/* 2、获取Bean对象 */
POJO pojo1 = app.getBean("pojo01", POJO.class);
POJO pojo2 = app.getBean("pojo01", POJO.class);
POJO pojo3 = app.getBean("pojo01", POJO.class);
/* 3、输出 */
System.out.println(pojo1);
System.out.println(pojo2);
System.out.println(pojo3);
}

/* 多例模式 */
@Test
public void test02(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("Scope/Beans.xml");
/* 2、获取Bean对象 */
POJO pojo1 = app.getBean("pojo02", POJO.class);
POJO pojo2 = app.getBean("pojo02", POJO.class);
POJO pojo3 = app.getBean("pojo02", POJO.class);
/* 3、输出 */
System.out.println(pojo1);
System.out.println(pojo2);
System.out.println(pojo3);
}


}

作用域

  • 当在 Spring 中定义一个 bean 时,你可以指定该 bean 的作用域。
    • 例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。
    • 同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。
作用域 描述
singleton 仅存在一个Bean实例,Bean以单例方式存在
(默认值)
prototype 每次从容器中调用Bean时,都返回一个新的实例,
(即每次调用getBean()时,相当于执行newXxxBean())
request 每次HTTP请求都会创建一个新的Bean
(仅适用于WebApplicationContext环境)
singleton 同一个HTTP Session共享一个Bean
不同Session使用不同的Bean
(仅适用于WebApplicationContext环境)
global-session 一般用于Portlet应用环境
(仅适用于WebApplicationContext环境)

目录结构

  • src
    • main
      • java.cn.water
        • POJO.java(实体类)
      • resources
        • Beans.xml(Spring配置文件)
    • test
      • java.cn.water.test
        • SpringTest.java(测试类)

实体类

1
2
3
4
5
6
7
8
package cn.water;

public class POJO {

/* 成员变量 */
private String message;

}

配置文件

  • 单例
    • bean标签
      • Scope属性:singleton
1
2
<!-- 单例 -->
<bean id="pojo01" class="cn.water.POJO" scope="singleton"></bean>
  • 多例
    • bean标签
      • Scope属性:prototype
1
2
<!-- 多例 -->
<bean id="pojo02" class="cn.water.POJO" scope="prototype"></bean>

测试类

  • 单例
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test01(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("LifeCycle/Beans.xml");
/* 2、获取Bean对象 */
POJO pojo1 = app.getBean("pojo01", POJO.class);
POJO pojo2 = app.getBean("pojo01", POJO.class);
POJO pojo3 = app.getBean("pojo01", POJO.class);
/* 3、输出 */
System.out.println(pojo1);
System.out.println(pojo2);
System.out.println(pojo3);
}
  • 多例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Test
    public void test02(){
    /* 1、加载配置文件,初始化Bean对象 */
    ApplicationContext app = new ClassPathXmlApplicationContext("LifeCycle/Beans.xml");
    /* 2、获取Bean对象 */
    POJO pojo1 = app.getBean("pojo02", POJO.class);
    POJO pojo2 = app.getBean("pojo02", POJO.class);
    POJO pojo3 = app.getBean("pojo02", POJO.class);
    /* 3、输出 */
    System.out.println(pojo1);
    System.out.println(pojo2);
    System.out.println(pojo3);
    }

运行结果

  • 单例
1
2
3
cn.water.POJO@6321e813
cn.water.POJO@6321e813
cn.water.POJO@6321e813
  • 多例
1
2
3
cn.water.POJO@6321e813
cn.water.POJO@79be0360
cn.water.POJO@22a67b4

生命周期

  • 理解 Spring bean 的生命周期很容易。当一个 bean 被实例化时,它可能需要执行一些初始化使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。
  • 为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。
  • Bean的生命周期可以表达为:
    • Bean的定义——Bean的初始化——Bean的使用——Bean的销毁

目录结构

  • src
    • main
      • java.cn.water
        • POJO.java(实体类)
      • resources
        • Beans.xml(Spring配置文件)
    • test
      • java.cn.water.test
        • SpringTest.java(测试类)

实体类

  • 在实体类中,添加两个成员方法
    • 初始化方法
    • 销毁方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package cn.water;

public class POJO {

/* 成员变量 */
private String message;

/* 成员方法:初始化 */
public void init(){
System.out.println("POJO类:初始化成功!");
}

/* 成员方法:销毁 */
public void destroy(){
System.out.println("POJO类:销毁成功!");
}

}

配置文件

  • 初始化方法
    • bean标签
      • init-method属性:方法名
  • 销毁方法
    • bean标签
      • destroy-method属性:方法名
1
<bean id="pojo" class="cn.water.POJO" init-method="init" destroy-method="destroy"></bean>

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test01(){

/* 1、加载配置文件,初始化Bean对象 */
AbstractApplicationContext abstractApp = new ClassPathXmlApplicationContext("LifeCycle/Beans.xml");

/* -- init-method 方法执行 --- */

/* 2、获取Bean对象 */
POJO pojo = abstractApp.getBean("pojo", POJO.class);

/* 3、注销容器 */
abstractApp.registerShutdownHook();

/* -- destroy-method 方法执行 --- */

}

运行结果

1
2
3
4
POJO类:初始化成功!


POJO类:销毁成功!

实例化

默认无参构造

实体类

1
2
3
4
5
6
7
8
9
10
11
12
package cn.water;

public class POJO {

/* 成员变量 */
private String message;

/* 构造方法 */
public POJO() {
System.out.println("POJO类:实例化成功!");
}
}

配置文件

  • 默认根据 无参构造函数 来创建Bean对象。
    • 实体类 全类名
1
<bean id="constructor_based" class="cn.water.POJO"></bean>

工厂类 成员方法

实体类

1
2
3
4
5
6
7
8
9
10
11
12
package cn.water;

public class POJO {

/* 成员变量 */
private String message;

/* 构造方法 */
public POJO() {
System.out.println("POJO类:实例化成功!");
}
}

工厂类

1
2
3
4
5
6
7
8
9
10
package cn.water;

public class POJOFactory {

/* 成员方法 */
public POJO getPOJO() {
return new POJO();
}

}

配置文件

  • 首先,根据 无参构造函数 来创建工厂类对象。
    • 工厂类 全类名
  • 然后,根据 工厂类的成员方法 来创建实体类对象。
    • 实体类 全类名
    • 工厂类 id
    • 工厂类 成员方法名
1
2
<bean id="factory" class="cn.water.POJOFactory"></bean>
<bean id="factory_Method_based" class="cn.water.POJO" factory-bean="factory" factory-method="getPOJO"></bean>

工厂类 静态方法

实体类

1
2
3
4
5
6
7
8
9
10
11
12
package cn.water;

public class POJO {

/* 成员变量 */
private String message;

/* 构造方法 */
public POJO() {
System.out.println("POJO类:实例化成功!");
}
}

工厂类

1
2
3
4
5
6
7
8
9
10
package cn.water;

public class POJOFactory {

/* 静态方法 */
public static POJO havePOJO(){
return new POJO();
}

}

配置文件

  • 直接根据 工厂类静态方法 来创建Bean对象。
    • 工厂类 全类名
    • 工厂类 静态方法名
1
<bean id="pojo03" class="cn.water.POJOFactory" factory-method="havePOJO"></bean>

Spring框架(2) —— 控制反转(IoC)

发表于 2019-08-13 更新于 2020-01-31 分类于 Java , Spring
本文字数: 12k 阅读时长 ≈ 11 分钟

简介

  • 本文分为四部分
    • 第一部分:介绍 内聚和耦合 的概念。
    • 第二部分:介绍Spring框架中的 IoC控制反转 机制。
    • 第三部分:介绍 ApplicationContext 和 BeanFactory 。
    • 第四部分:比较 ApplicationContext 和 BeanFactory 。

内聚和耦合

在介绍Spring IoC容器之前,我们先要了解一下软件设计好坏的评判标准:耦合和内聚。

耦合

  • 耦合性(Coupling)是对模块间关联程度的度量。
  • 耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差。

高耦合

  • 手机和充电线是高耦合,为什么呢?
  • 打个比方,苹果手机和安卓手机必须使用本机适配的充电线,而且在安卓手机中甚至还分为USB接口和Type-C接口。如果我使用苹果充电接口给安卓手机充电,那么苹果充电线无法正常输电,安卓手机也无法正常充电了。(忽略其他影响)
  • 由此可见,手机和充电接口之间的依赖关系紧密,独立性差,所以属于高耦合。

低耦合

  • 充电接口和充电接口是低耦合。
  • 打个比方,不论是苹果充电接口还是安卓充电接口,都可以使用苹果充电接头,也都可以使用安卓充电接头。(忽略其他影响)
  • 由此可见,充电接口和充电接头之间的依赖关系松散,独立性高,所以属于低耦合。

内聚

  • 内聚(Cohesion)是一个模块内部各成分间关联程度的度量。
  • 内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。
  • 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。

低内聚

  • 职责混乱是低内聚。
  • 打个比方,开发人员除了开发项目,还要参与需求分析;测试人员除了测试项目,还要参与开发。这表示每一个内聚模块都没有做好自己分内的事,因为如果每个内聚模块都做好了自己该做的事情,别的模块是不会有插手的余地的。

高内聚

  • 各司其职是高内聚。
  • 打个比方,软件开发人员完成项目开发,测试人员完成项目测试。这表示每一个内聚模块都做好了自己分内的事情,不需要别的内聚模块来插手,也不需要去别的内裤模块插手。
阅读全文 »

Spring框架(1) —— 概述

发表于 2019-08-12 更新于 2020-01-31 分类于 Java , Spring
本文字数: 2.3k 阅读时长 ≈ 2 分钟

概述

  • Spring是分层的企业级full-stack轻量级开源框架,以IoC和AOP为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,并整合了开源世界中众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。

控制反转 IoC(Inversion of Control )

  • 控制反转 就是对象之间的依赖关系由容器来创建,对象之间的关系本来是由我们开发者自己创建和维护的,在我们使用Spring框架后,对象之间的关系由容器来创建和维护,将开发者做的事让容器做,这就是控制反转。BeanFactory接口是Spring Ioc容器的核心接口。
  • 换句话说,就是指new实例工作不再由程序员来做,而是交给Spring容器来做。new实例工作的控制权不再由程序员掌控。

面向方面的程序设计 AOP(Aspect-Oriented Programming)

  • Spring 框架的一个关键组件是面向方面的程序设计(AOP)框架。
  • 一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。
  • 在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是方面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。
  • Spring 框架的 AOP 模块提供了面向方面的程序设计实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。
阅读全文 »

MyBatis框架(14) —— 缓存机制(基于注解)

发表于 2019-08-10 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 5.9k 阅读时长 ≈ 5 分钟

目录结构

src

  • main
    • java
      • cn.water.dao
        • UserDao.java(持久层接口)
      • cn.water.domain
        • User.java(实体类)
    • resources
      • SqlMapConfig.xml(MyBatis主配置文件)
      • jdbcConfig.properties(数据库连接信息文件)
  • test
    • java.cn.water
      • MybatisTest.java(测试类)

MyBatis配置文件

jdbcConfig.properties

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

SqlMapConfig.xml

  • 开启 二级缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 配置参数 -->
<settings>
<!-- 开启 二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!-- 指定包:持久层接口 -->
<mappers>
<package name="cn.water.dao"/>
</mappers>

</configuration>

实体类

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package cn.water.domain;

import java.io.Serializable;
import java.util.Date;

/**
* @author Water
* @date 2019/10/12 - 7:51
* @description
*/
public class User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

@Override
public String toString() {
return "User{" +
"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;
}
}

持久层接口

UserDao.java

  • 注解支持:@CacheNamespace(blocking = true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.water.dao;

import cn.water.domain.User;

/**
* @author Water
* @date 2019/10/12 - 7:51
* @description
*/
@CacheNamespace(blocking = true)
public interface UserDao {

@Select("SELECT * FROM user WHERE id = #{userId}")
User findById(Integer userId);

}

测试类

MyBatisT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package cn.water;

import cn.water.dao.UserDao;
import cn.water.domain.User;
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;

/**
* @author Water
* @date 2019/10/12 - 7:56
* @description
*/
public class MybatisTest {


/* 成员变量 */
private InputStream inputStream;
private SqlSessionFactory factory;
private SqlSession session;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession(true);/* 设置自动提交 */
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}

/** 一级缓存:同一个 SqlSession */
@Test
public void test01(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);
User user02 = dao.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02)); //true
}

/** 一级缓存:不同 SqlSession */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
User user01 = dao.findById(41);
System.out.println("第一次查询:"+user01);

/* commit()(执行插入、更新、删除)、close() */
session.close();
session = factory.openSession();

UserDao dao02 = session.getMapper(UserDao.class);
User user02 = dao02.findById(41);
System.out.println("第二次查询:"+user02);
System.out.println("两次查询结果是否相等:"+(user01==user02));//false
}


}

开启二级缓存(缓存)

  • 二级缓存的使用步骤:
    • 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    • 第二步:让当前的持久层接口支持二级缓存(在UserDao.java中配置)

Mybatis框架(13) —— 实现多表查询的延迟加载操作(基于注解)

发表于 2019-08-10 更新于 2020-01-31 分类于 Java , MyBatis
本文字数: 12k 阅读时长 ≈ 11 分钟

简介

MyBatis中多表之间的关系

  • 本次案例主要以最为简单的用户(User)、账户(Account)、角色(Role)的模型来分析Mybatis多表关系。
    • 用户为User 表,账户为Account表。
      • 一对多关系
        • 一个用户(User)可以开设多个账户(Account)
          • 从查询 用户信息(User) 出发,关联查询 账户信息(Account)属于一对多查询
      • 一对一关系(多对一)
        • 多个账户(Account)可以对应一个用户(User)
        • 单个账户(Account)只能属于一个用户(User)
          • 从查询 账户信息(Account) 出发,关联查询 用户信息(User)属于一对一查询
      • 多对多关系
        • 一个用户(User)可以拥有多个角色(Role)
        • 一个角色(Role)可以赋予多个用户(User)
          • 多对多关系其实我们看成是双向的一对多关系。

目录结构

  • src
    • main
      • java
        • cn.water.dao
          • UserDao.java(一对多 持久层接口)
          • AccountDao.java(一对一 持久层接口)
        • cn.water.domain
          • User.java(一对多 实体类)
          • Account.java(一对一 实体类)
      • resources
        • cn.water.dao
          • User.xml(一对多 映射配置文件)
          • Account.xml(一对一 映射配置文件)
        • SqlMapConfig.xml(MyBatis主配置文件)
        • jdbcConfig.properties(数据库连接信息文件)
    • test
      • java.cn.water
        • MybatisTest.java(测试类)

MyBatis主配置文件

jdbcConfig.properties

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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">


<!-- mybatis的主配置文件 -->
<configuration>

<!-- 外部配置 -->
<properties resource="jdbcConfig.properties"></properties>

<!-- 配置参数 -->
<settings>
<!-- 开启 延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

<!-- 指定包:实体类-->
<typeAliases>
<package name="cn.water.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!-- 指定包:持久层接口 -->
<mappers>
<package name="cn.water.dao"/>
</mappers>

</configuration>

实体类

Account.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package cn.water.domain;

import java.io.Serializable;

/**
* @author Water
* @date 2019/10/13 - 15:10
* @description
*/
public class Account implements Serializable {

private Integer id;
private Integer uid;
private Double money;
private User user;

@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
", user=" + user +
'}';
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Integer getUid() {
return uid;
}

public void setUid(Integer uid) {
this.uid = uid;
}

public Double getMoney() {
return money;
}

public void setMoney(Double money) {
this.money = money;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}
}

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package cn.water.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
* @author Water
* @date 2019/10/13 - 15:10
* @description
*/
public class User implements Serializable {

private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", accounts=" + accounts +
'}';
}

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 List<Account> getAccounts() {
return accounts;
}

public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
}

持久层接口

AccountDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package cn.water.dao;

import cn.water.domain.Account;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
* @author Water
* @date 2019/10/13 - 15:11
* @description 一对一查询 延迟查询
*/
public interface AccountDao {

/* 一对一查询:查询所有账户信息和对应的用户信息 */
@Select("SELECT * FROM account")
@Results(id = "accountMap",
value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
/* 传递值:uid,变量名:user */
@Result(column = "uid",
property = "user",
one = @One(select = "cn.water.dao.UserDao.findById",
fetchType = FetchType.LAZY
)
)

})
List<Account> findAll();


/* 一对多查询:根据id,查询账户信息 */
@Select("SELECT * FROM account WHERE uid = #{id}")
Account findByUid(Integer id);

}

UserDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package cn.water.dao;

import cn.water.domain.User;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
* @author Water
* @date 2019/10/13 - 15:11
* @description 一对多查询 延迟查询
*/
public interface UserDao {

/* 一对一查询:按照id,查询用户信息 */
@Select("SELECT * FROM user WHERE id = #{id}")
User findById();

/* 一对多查询:查询所有的用户信息和对应的账户信息 */
@Select("SELECT * FROM user")
@Results(id = "userMap",value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
/* 传递值:uid,变量名:accounts */
@Result(column = "id",
property = "accounts",
many = @Many(select = "cn.water.dao.AccountDao.findByUid",
fetchType = FetchType.LAZY
)
)
})
List<User> findAll();

}

测试类

MyBatisTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package cn.water;

import cn.water.dao.AccountDao;
import cn.water.dao.UserDao;
import cn.water.domain.Account;
import cn.water.domain.User;
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.Date;

/**
* @author Water
* @date 2019/10/13 - 10:48
* @description
*/
public class UserTest {

private InputStream inputStream;
private SqlSessionFactory factory;
private SqlSession session;


@Before
public void init() throws IOException {
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
session = factory.openSession(true);
}

@After
public void destroy () throws IOException {
session.close();
inputStream.close();
}


/** 一对一查询 */
@Test
public void test01(){
AccountDao dao = session.getMapper(AccountDao.class);
for (Account account : dao.findAll()) {
System.out.println(account);
}
}


/** 一对多查询 */
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
for (User user : dao.findAll()) {
System.out.println(user);
}
}



}

一对一查询(延迟)

  • @One注解代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
  • 注解
    • @One:实现一对一结果集封装
      • select:指定用来多表查询的sqlmapper
      • fetchType:覆盖全局的配置参数(延迟加载:lazyLoadingEnabled)

账户类 持久层接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Select("SELECT * FROM account")
@Results(id = "accountMap",
value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
/* 传递值:uid,变量名:user */
@Result(column = "uid",
property = "user",
one = @One(select = "cn.water.dao.UserDao.findById",
fetchType = FetchType.LAZY
)
)

})
List<Account> findAll();

用户类 持久层接口

1
2
@Select("SELECT * FROM user WHERE id = #{id}")
User findById();

测试类

1
2
3
4
5
6
7
@Test
public void test01(){
AccountDao dao = session.getMapper(AccountDao.class);
for (Account account : dao.findAll()) {
System.out.println(account);
}
}

运行结果

![](13.注解 多表查询 延迟加载\注解 一对一查询 延迟查询.png)

一对多查询(延迟)

  • @Many注解代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
  • 注解
    • @Many:实现一对多结果集封装
      • select:指定用来多表查询的sqlmapper
      • fetchType:覆盖全局的配置参数(延迟加载:lazyLoadingEnabled)

用户类 持久层接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Select("SELECT * FROM user")
@Results(id = "userMap",value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
/* 传递值:uid,变量名:accounts */
@Result(column = "id",
property = "accounts",
many = @Many(select = "cn.water.dao.AccountDao.findByUid",
fetchType = FetchType.LAZY
)
)
})
List<User> findAll();

账户类 持久层接口

1
2
@Select("SELECT * FROM account WHERE uid = #{id}")
Account findByUid(Integer id);

测试类

1
2
3
4
5
6
7
@Test
public void test02(){
UserDao dao = session.getMapper(UserDao.class);
for (User user : dao.findAll()) {
System.out.println(user);
}
}

运行结果

![](13.注解 多表查询 延迟加载\注解 一对多查询 延迟查询.png)

1234…7
water.

water.

Few words,many deeds.
68 日志
21 分类
43 标签
GitHub E-Mail
Links
  • 残夜
  • Morty
  • Tmiracle
  • Dxoca
  • BlankYk
  • UncleGood
0%
© 2018 – 2020 water. | 658k | 9:58
|