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
package Exer.java;

import org.junit.Test;

public class Singleton {

/**
* 饿汉式
*/
@Test
public void testBank() {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
}

/**
* 懒汉式
*/
@Test
public void testCustomer() {
Customer customer1 = Customer.getInstance();
Customer customer2 = Customer.getInstance();
System.out.println(customer1 == customer2);
}
}

class Bank {

// 1.私有化类的空参构造器
private Bank() {
}

// 2.在类的内部创建一个对象,要求这个对象是静态的,"3"的静态方法只能访问静态成员变量
private static final Bank instance = new Bank();

// 3.在类的内部提供一个公共的且是静态的方法返回一个实例
public static Bank getInstance() {
return instance;
}
}

class Customer {

// 1.私有化类的空参构造器
private Customer() {

}

// 2.声明此对象,没有初始化,"3"的静态方法只能访问静态成员变量
private static Customer instance = new Customer();

// 3.声明 public static 返回当前类对象的方法
public static synchronized Customer getInstance() {
if (instance == null) {
instance = new Customer();
}
return instance;

}
}
点击查看运行结果
1
2
3
4
true
true

Process finished with exit code 0

程序中成员变量赋值的执行顺序的例子

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
/**
* 1.声明成员变量的默认初始化
* 2.显式初始化、多个初始化块依次被执行(同级别下按先后顺序执行)
* 3.构造器再对成员进行初始化操作
* 4.通过”对象.属性”或”对象.方法”的方式,可多次给属性赋值
*/
public class LeafTest {
public static void main(String[] args) {
new Leaf();
System.out.println("---------------------------分割线---------------------------");
new Leaf();
}
}

class Root {
static {
System.out.println("Root的静态初始化块");
}

{
System.out.println("Root的普通初始化块");
}

public Root() {
System.out.println("Root的无参数的构造器");
}
}

class Mid extends Root {
static {
System.out.println("Mid的静态初始化块");
}

{
System.out.println("Mid的普通初始化块");
}

public Mid() {
System.out.println("Mid的无参数的构造器");
}

public Mid(String msg) {
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}

class Leaf extends Mid {
static {
System.out.println("Leaf的静态初始化块");
}

{
System.out.println("Leaf的普通初始化块");
}

public Leaf() {
//通过super调用父类中有一个字符串参数的构造器
super("Jermyn");
System.out.println("Leaf的构造器");
}
}
点击查看运行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:Jermyn
Leaf的普通初始化块
Leaf的构造器
---------------------------分割线---------------------------
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:Jermyn
Leaf的普通初始化块
Leaf的构造器

Process finished with exit code 0

创建线程的四种方式

JDK1.5 之前创建新执行线程有两种方法:①继承Thread类的方式;②实现Runnable接口的方式;
JDK1.5 ③新增实现Callable创建方式

  1. 定义子类继承Thread类。
  2. 子类中重写Thread类中的run方法。
  3. 创建Thread子类对象,即创建了线程对象。
  4. 调用线程对象start方法:启动线程,调用run方法。
    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
    //1. Declare a class to be a subclass of Thread
    class EvenNumber extends Thread {

    int number = 0;

    public EvenNumber(int number) {
    this.number = number;
    }

    // 2. Override the run method of class Thread.
    @Override
    public void run() {
    for (int i = 0; i < number; i++) {
    if (i % 2 == 0) {
    System.out.println(Thread.currentThread().getName() + ":" + i + " is a even number");

    }
    }
    }
    }

    public class ThreadTest01 {

    public static void main(String[] args) {

    // 3.Build an instance of the subclass
    EvenNumber n = new EvenNumber(10);

    // 4. This instance can be allocated and started
    n.start();

    // This code will be executed in the main method
    for (int i = 0; i < n.number; i++) {
    if (i % 2 == 0) {
    System.out.println(Thread.currentThread().getName() + ":" + i + " is a even number");
    }
    }
    }
    }
    点击查看运行结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    D:\PATH-EN\java-path\bin\java.exe ...
    main:0 is a even number
    Thread-0:0 is a even number
    Thread-0:2 is a even number
    Thread-0:4 is a even number
    Thread-0:6 is a even number
    Thread-0:8 is a even number
    main:2 is a even number
    main:4 is a even number
    main:6 is a even number
    main:8 is a even number

    Process finished with exit code 0
  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
    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
    // 1. Create a thread is to declare a class that implements the Runnable interface.
    class PrimeRun implements Runnable {
    long minPrime;

    PrimeRun(long minPrime) {
    this.minPrime = minPrime;
    }


    // 2.This class then implements the run method.
    @Override
    public void run() {
    // Compute primes larger than minPrime
    }
    }

    public class ThreadTest02 {

    public static void main(String[] args) {
    // 3. Create an instance of PrimeRun class
    PrimeRun p = new PrimeRun(143);

    // When you create a thread put the instance as arguments and start it
    new Thread(p).start();
    new Thread(p).start();
    }
    }
  1. 创建一个实现Callable的实现类
  2. 实现call方法,将此线程需要执行的操作声明在call()中
  3. 创建Callable接口实现类的对象
  4. 将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
  5. 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
  6. 获取Callable中call方法的返回值get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
    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
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;

    public class ThreadCallable {

    public static void main(String[] args) {
    // 3.创建Callable接口实现类的对象
    SumOfEvenNumber sumOfEvenNumber = new SumOfEvenNumber();

    // 4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
    FutureTask futureTask = new FutureTask(sumOfEvenNumber);

    // 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
    new Thread(futureTask).start();

    try {
    // 6.获取Callable中call方法的返回值get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
    System.out.println("sum=" + futureTask.get());
    } catch (InterruptedException e) {
    e.printStackTrace();
    } catch (ExecutionException e) {
    e.printStackTrace();
    }
    }
    }

    // 1.创建一个实现Callable的实现类
    class SumOfEvenNumber implements Callable {

    // 2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {

    int sum = 0;
    for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
    System.out.println(i);
    sum += i;
    }
    }
    return sum;
    }
    }
    点击查看运行结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    D:\PATH-EN\java-path\bin\java.exe ...
    0
    2
    4
    6
    8
    sum=20

    Process finished with exit code 0
  1. 提供指定线程数量的线程池
    设置线程池的属性
  2. 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
  3. 关闭连接池
    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
    public class ThreadPoolTest {

    public static void main(String[] args) {

    // 1. 提供指定线程数量的线程池
    ExecutorService service = Executors.newFixedThreadPool(10);

    // 2. 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
    service.execute(new EvenNum());
    service.execute(new OddNumber());

    // 3. 关闭连接池
    service.shutdown();
    // service.submit();

    }
    }

    class EvenNum implements Runnable {

    @Override
    public void run() {
    for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
    System.out.println(Thread.currentThread().getName() +
    ":" + i);
    }
    }
    }
    }

    class OddNumber implements Runnable {

    @Override
    public void run() {
    for (int i = 0; i < 10; i++) {
    if (i % 2 != 0) {
    System.out.println(Thread.currentThread().getName() +
    ":" + i);
    }
    }
    }
    }
    点击查看运行结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    D:\PATH-EN\java-path\bin\java.exe ...
    pool-1-thread-1:0
    pool-1-thread-1:2
    pool-1-thread-1:4
    pool-1-thread-1:6
    pool-1-thread-1:8
    pool-1-thread-2:1
    pool-1-thread-2:3
    pool-1-thread-2:5
    pool-1-thread-2:7
    pool-1-thread-2:9

    Process finished with exit code 0

方式一和方式二相比:

  1. 避免了单继承的局限性
  2. 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

方式二和方式三相比:

  1. 与使用Runnable相比, Callable功能更强大
  2. 相比run()方法,可以有返回值
  3. 方法可以抛出异常
  4. 支持泛型的返回值
  5. 需要借助FutureTask类,比如获取返回结果

使用线程池的好处:

  1. 提高响应速度(减少了创建新线程的时间)
  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  3. 便于线程管理
    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

死锁示例

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
public class DeadLockTest {

public static void main(String[] args) {

StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();

new Thread() {

@Override
public void run() {

synchronized (s1) {
s1.append("a");
s2.append("1");

try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized (s2) {
s1.append("b");
s2.append("2");

System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();


new Thread(new Runnable() {

@Override
public void run() {
synchronized (s2) {
s1.append("c");
s2.append("3");

try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized (s1) {
s1.append("d");
s2.append("4");

System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
点击查看运行结果
1
2
D:\PATH-EN\java-path\bin\java.exe ...

反射的应用:动态代理

静态代理

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
interface ClothFactory {

void produceCloth();

}

// 代理类
class ProxyClothFactory implements ClothFactory {

private ClothFactory factory;

public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}

@Override
public void produceCloth() {
System.out.println("代理工厂准备工作");

factory.produceCloth();

System.out.println("代理工厂做收尾工作");
}
}

// 被代理类
class AntaClothFactory implements ClothFactory {

@Override
public void produceCloth() {
System.out.println("安踏工厂生产产品");
}
}

public class StaticProxyTest {

public static void main(String[] args) {

AntaClothFactory anta = new AntaClothFactory();

ProxyClothFactory pcf = new ProxyClothFactory(anta);

pcf.produceCloth();
}
}

点击查看运行结果
1
2
3
4
5
6
D:\PATH-EN\java-path\bin\java.exe ...
代理工厂准备工作
安踏工厂生产产品
代理工厂做收尾工作

进程已结束,退出代码0
  • 代理设计模式的原理:使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原 始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原 始对象上。
  • 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时 根据需要动态创建目标类的代理对象。
  • 动态代理使用场合:

    • 调试
    • 远程方法调用
  • 动态代理相比于静态代理的优点:抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中 处理,这样,我们可以更加灵活和统一的处理众多的方法。

  • Java动态代理相关API

    • Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
    • 提供用于创建动态代理类和动态代理对象的静态方法
      • static Class<?>getProxyClass(ClassLoader loader, Class<?>… interfaces) 创建一个动态代理类所对应的Class对象
      • static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 直接创建一个动态代理对象
  • 动态代理步骤

  1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
  2. 创建被代理的类以及接口
  3. 通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
  4. 通过 Subject代理调用RealSubject实现类的方法

    动态代理

    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
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    interface Human {

    String getBelief();

    void eat(String food);
    }

    //2. 创建被代理的类以及接口
    class SuperMan implements Human {

    @Override
    public String getBelief() {
    return "Peace and love!";
    }

    @Override
    public void eat(String food) {
    System.out.println("我喜欢吃" + food);
    }
    }


    class ProxyFactory {

    public static Object getProxyInstance(Object obj) {

    MyInvocationHandler handler = new MyInvocationHandler();

    handler.bind(obj);
    // 3.通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
    return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
    }

    //1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
    class MyInvocationHandler implements InvocationHandler {

    //obj:被代理类的对象
    private Object obj;

    public void bind(Object obj) {
    this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
    //obj:被代理类的对象
    Object invoke = method.invoke(obj, args);

    //上述方法的返回值就作为当前类中的invoke()的返回值。
    return invoke;
    }
    }

    public class ProxyTest {

    public static void main(String[] args) {

    SuperMan superMan = new SuperMan();
    //4. 通过ProxyFactory代理调用getProxyInstance实现类的方法
    Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);

    String belief = proxyInstance.getBelief();
    System.out.println(belief);

    proxyInstance.eat("KFC");
    }
    }

    点击查看运行结果
    1
    2
    3
    4
    5
    D:\PATH-EN\java-path\bin\java.exe ...
    Peace and love!
    我喜欢吃KFC

    进程已结束,退出代码0

    SQL相关

    7种SQL JOINS的实现


语法格式小结

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
# 1. 左中图
#实现A - A∩B
select 字段列表
from A表 left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;

# 2. 右中图
#实现B - A∩B
select 字段列表
from A表 right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;

# 3. 左下图
#实现查询结果是A∪B
#用左外的A,union 右外的B
select 字段列表
from A表 left join B表
on 关联条件
where 等其他子句
union
select 字段列表
from A表 right join B表
on 关联条件
where 等其他子句;

# 4. 右下图
#实现A∪B - A∩B 或 (A - A∩B) ∪ (B - A∩B)
#使用左外的 (A - A∩B) union 右外的(B - A∩B)
select 字段列表
from A表 left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句
union
select 字段列表
from A表 right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句

JDBC

使用PreparedStatement实现CURD操作

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package Exer.curd;

import Exer.bean.Customer;
import Exer.bean.Order;
import Exer.junit.JDBCUtils;
import org.junit.Test;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class GeneralForQuery {

/**
* 通用的增、删、改操作(体现一:增、删、改 ; 体现二:针对于不同的表)
*
* @param sql 包含占位符的sql语句
* @param args 修改的字段
*/
public void update(String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库的连接
conn = JDBCUtils.getConnection();

//2.获取PreparedStatement的实例 (或:预编译sql语句)
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}

//4.执行sql语句
ps.execute();
} catch (Exception e) {

e.printStackTrace();
} finally {
//5.关闭资源
JDBCUtils.closeResource(ps, conn);
}
}

/**
* 通用的查找操作
*
* @param tClass 泛型,想查询的表的class
* @param sql 包含占位符的sql语句
* @param args 查询的字段
* @param <T> 泛型,返回指定的实例
* @return
*/
public <T> List<T> generalForQuery(Class<T> tClass, String sql, Object... args) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//TODO 获取数据库的连接
connection = JDBCUtils.getConnection();

//TODO 预编译sql语句,返回 PrepareStatement 实例
ps = connection.prepareStatement(sql);

//TODO 填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}

//TODO 执行,并返回结果集
rs = ps.executeQuery();

// TODO 查询的资源的列数封装的结果集的元数据中
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();

// TODO 创建集合对象
ArrayList<T> list = new ArrayList<>();

// TODO 处理结果集,判断结果集的下一条是否有数据,有的话返回 true 否则返回 false
while (rs.next()) {
T t = tClass.newInstance();

for (int i = 0; i < columnCount; i++) {
// 获取这一列的列的别名,使用元数据的getColumnLabel方法获取列的别名,getColumnName是获取列的字段名
String columnLabel = rsmd.getColumnLabel(i + 1);

// 获取这一列的列名
Object columnValue = rs.getObject(i + 1);

// TODO 给 customer 对象指定的属性 columnName 赋值为 columnValue,使用反射
Field declaredField = tClass.getDeclaredField(columnLabel);

// 属性名可能是私有的
declaredField.setAccessible(true);

// 根据属性名设置属性值
declaredField.set(t, columnValue);
}
list.add(t);
}
return list;
} catch (IOException | IllegalAccessException | IllegalArgumentException | SecurityException |
NoSuchFieldException | SQLException | ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException ignored) {

} finally {
JDBCUtils.closeResource(ps, connection, rs);
}
return null;
}


/**
* 通用的查询方式测试
*/
@Test
public void testGeneralForQuery() {

// 针对 order 表的查询方式
String sql1 = "select order_id as orderID, order_name as orderName, order_date as orderDate from `order` where order_id = ?";
List<Order> orders = generalForQuery(Order.class, sql1, 2);
for (Order order : orders) {
System.out.println(order);
}

// 针对 customers 表的查询方式
String sql2 = "select id,name,email from customers where id < ?";
List<Customer> customers = generalForQuery(Customer.class, sql2, 5);
customers.forEach(System.out::println);
}

/**
* 通用的增删改操作(同一表)
*/
@Test
public void testUpdate() {
// 删除 id=?的customer
String sql1 = "delete from customers where id = ? ";
update(sql1, 19);

// 修改 order 表的 order_id = ? 的姓名
String sql2 = "update `order` set order_name = ? where order_id = ?";
update(sql2, "bb", 2);
}
}

点击查看运行结果
1
2
3
4
5
6
7
8
9
com.mysql.cj.jdbc.ConnectionImpl@1e6454ec
Order{orderID=2, orderName='bb', orderDate=2000-02-01}
com.mysql.cj.jdbc.ConnectionImpl@3e11f9e9
Customer{id=1, name='汪峰', email='wf@126.com', birth=null}
Customer{id=2, name='王菲', email='wangf@163.com', birth=null}
Customer{id=3, name='林志玲', email='linzl@gmail.com', birth=null}
Customer{id=4, name='汤唯', email='tangw@sina.com', birth=null}

Process finished with exit code 0
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
81
package Exer.junit;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils {

public static Connection getConnection() throws IOException, ClassNotFoundException, SQLException {
//1.加载配置文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);

//2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");

//3.加载驱动
Class.forName(driverClass);

//4.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
return conn;
}

/**
* @param ps
* @param connection
*/
public static void closeResource(Statement ps, Connection connection) {
//TODO 资源关闭
try {
if (ps != null) {
ps.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}

try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

public static void closeResource(Statement ps, Connection connection, ResultSet rs) {
//TODO 资源关闭
try {
if (ps != null) {
ps.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}

try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}

try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

考虑到数据库事务的情况下的通用操作

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package Exer.connection;

import Exer.junit.JDBCUtils;
import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.*;

/*
* 1.事务处理的原则:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。
* 当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存
* 下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。
*
* 2.数据一旦提交,就不可回滚
*
* 3.哪些操作会导致数据的自动提交?
* >DDL操作一旦执行,都会自动提交。
* >set autocommit = false 对DDL操作失效
* >DML默认情况下,一旦执行,就会自动提交。
* >我们可以通过set autocommit = false的方式取消DML操作的自动提交。
* >默认在关闭连接时,会自动的提交数据
*/
public class TransactionSqlGeneral {

//******************未考虑数据库事务情况下的转账操作**************************
/*
* 针对于数据表user_table来说:
* AA用户给BB用户转账100
*
* update user_table set balance = balance - 100 where user = 'AA';
* update user_table set balance = balance + 100 where user = 'BB';
*/
@Test
public void testUpdate() {

String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(sql1, "AA");

//模拟网络异常
System.out.println(10 / 0);

String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(sql2, "BB");

System.out.println("转账成功");
}

// 通用的增删改操作---version 1.0
public int update(String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!
Connection conn = null;
PreparedStatement ps = null;
try {
// 1.获取数据库的连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);// 小心参数声明错误!!
}
// 4.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {

//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}

// 5.资源的关闭
JDBCUtils.closeResource(ps, conn);

}
return 0;

}

//********************考虑数据库事务后的转账操作*********************

@Test
public void testUpdateWithTx() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
//1.取消数据的自动提交
conn.setAutoCommit(false);

String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn, sql1, "AA");

//模拟网络异常
System.out.println(10 / 0);

String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn, sql2, "BB");

System.out.println("转账成功");

//2.提交数据
conn.commit();

} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {

JDBCUtils.closeResource(null, conn);
}

}

// 通用的增删改操作---version 2.0 (考虑上事务)
public int update(Connection conn, String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
try {
// 1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
// 2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);// 小心参数声明错误!!
}
// 3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4.资源的关闭
JDBCUtils.closeResource(ps, null);

}
return 0;

}

//*****************************************************
@Test
public void testTransactionSelect() throws Exception {

Connection conn = JDBCUtils.getConnection();
//获取当前连接的隔离级别
System.out.println(conn.getTransactionIsolation());
//设置数据库的隔离级别:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
//取消自动提交数据
conn.setAutoCommit(false);

String sql = "select user,password,balance from user_table where user = ?";
User user = getInstance(conn, User.class, sql, "CC");

System.out.println(user);

}

@Test
public void testTransactionUpdate() throws Exception {
Connection conn = JDBCUtils.getConnection();

//取消自动提交数据
conn.setAutoCommit(false);
String sql = "update user_table set balance = ? where user = ?";
update(conn, sql, 5000, "CC");

Thread.sleep(15000);
System.out.println("修改结束");
}

//通用的查询操作,用于返回数据表中的一条记录(version 2.0:考虑上事务)
public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {

ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}

rs = ps.executeQuery();
// 获取结果集的元数据 :ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();

if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);

// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);

// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(ps, null, rs);

}

return null;
}

}

数据库连接池之一:C3P0数据库连接池

C3P0数据库连接池


1
2
3
4
5
6
7
8
9
10
11
12
13
//使用C3P0数据库连接池的方式,获取数据库的连接:不推荐
public static Connection getConnection1() throws Exception{
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test");
cpds.setUser("root");
cpds.setPassword("abc123");

// cpds.setMaxPoolSize(100);

Connection conn = cpds.getConnection();
return conn;
}


1
2
3
4
5
6
7
//使用C3P0数据库连接池的配置文件方式,获取数据库的连接:推荐
// new在外面,只需要生成一次就可以
private static DataSource cpds = new ComboPooledDataSource("helloc3p0");
public static Connection getConnection2() throws SQLException{
Connection conn = cpds.getConnection();
return conn;
}

其中,src下的配置文件为:【c3p0-config.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
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="helloc3p0">
<!-- 获取连接的4个基本信息 -->
<property name="user">root</property>
<property name="password">abc123</property>
<property name="jdbcUrl">jdbc:mysql:///test</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>

<!-- 涉及到数据库连接池的管理的相关属性的设置 -->
<!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">5</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">10</property>
<!-- C3P0 数据库连接池可以维护的 Statement 的个数 -->
<property name="maxStatements">20</property>
<!-- 每个连接同时可以使用的 Statement 对象的个数 -->
<property name="maxStatementsPerConnection">5</property>

</named-config>
</c3p0-config>

数据库连接池之二:DBCP数据库连接池

  • DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
    • Commons-dbcp.jar:连接池的实现
    • Commons-pool.jar:连接池实现的依赖库
  • Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
  • 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
  • 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
  • 配置属性说明
属性默认值说明
initialSize0连接池启动时创建的初始化连接数量
maxActive8连接池中可同时连接的最大的连接数
maxIdle8连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle0连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。
maxWait无限制最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatementsfalse开启池的Statement是否prepared
maxOpenPreparedStatements无限制开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout300超过时间限制,回收没有用(废弃)的连接
removeAbandonedfalse超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收


1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Connection getConnection3() throws Exception {
BasicDataSource source = new BasicDataSource();

source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///test");
source.setUsername("root");
source.setPassword("abc123");

//
source.setInitialSize(10);

Connection conn = source.getConnection();
return conn;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//使用dbcp数据库连接池的配置文件方式,获取数据库的连接:推荐
private static DataSource source = null;
static{
try {
Properties pros = new Properties();

InputStream is = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");

pros.load(is);
//根据提供的BasicDataSourceFactory创建对应的DataSource对象
source = BasicDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}

}
public static Connection getConnection4() throws Exception {

Connection conn = source.getConnection();

return conn;
}

其中,src下的配置文件为:【dbcp.properties】
1
2
3
4
5
6
7
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=abc123

initialSize=10
#...

Druid(德鲁伊)数据库连接池

Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.atguigu.druid;

import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

public class TestDruid {
public static void main(String[] args) throws Exception {
Properties pro = new Properties(); pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}

其中,src下的配置文件为:【druid.properties】
1
2
3
4
5
6
7
8
9
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=20
maxWait=1000
filters=wall

详细配置参数:

  • 详细配置参数:
配置缺省说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

Apache-DBUtils实现CRUD操作

  • commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

  • API介绍:

    • org.apache.commons.dbutils.QueryRunner
    • org.apache.commons.dbutils.ResultSetHandler
    • 工具类:org.apache.commons.dbutils.DbUtils
  • API包说明:

  • 主要API的使用
    DbUtils
    DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:

  • public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
  • public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
  • public static void commitAndClose(Connection conn)throws SQLException: 用来提交连接的事务,然后关闭连接
  • public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
  • public static void rollback(Connection conn)throws SQLException:允许conn为null,因为方法内部做了判断
  • public static void rollbackAndClose(Connection conn)throws SQLException
  • rollbackAndCloseQuietly(Connection)
  • public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。

QueryRunner类

  • 该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

  • QueryRunner类提供了两个构造器:

    • 默认的构造器
    • 需要一个 javax.sql.DataSource 来作参数的构造器
  • QueryRunner类的主要方法:

    • 更新
      • public int update(Connection conn, String sql, Object… params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
      • ……
    • 插入
      • publicT insert(Connection conn,String sql,ResultSetHandlerrsh, Object… params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值
      • ….
    • 批处理
      • public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
      • publicT insertBatch(Connection conn,String sql,ResultSetHandlerrsh,Object[][] params)throws SQLException:只支持INSERT语句
      • …..
    • 查询
      • public Object query(Connection conn, String sql, ResultSetHandler rsh,Object… params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
      • ……

使用德鲁伊数据库连接池进行最终版本测试

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package Exer.curd;

import Exer.bean.Customer;
import Exer.junit.JDBCUtils;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.*;
import org.junit.Test;

import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class GeneralTest {

/**
* 向 Customers 表中插入一条数据
*/
@Test
public void testInsert() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth)values(?,?,?)";
int insertCount = runner.update(conn, sql, "蔡徐坤", "caixukun@126.com", "1997-09-08");
System.out.println("添加了" + insertCount + "条记录");
} catch (Exception e) {
e.printStackTrace();
} finally {

JDBCUtils.closeResource(null, conn, null);
}
}

/**
* BeanHandler:查询 Customers 表中的一条记录
*/
@Test
public void testQueryInfo() throws SQLException {

Connection connection = null;
try {

connection = JDBCUtils.getConnection();

QueryRunner queryRunner = new QueryRunner();

String sql = "select id,name,email,birth from customers where id = ?";
BeanHandler<Customer> customerBeanHandler = new BeanHandler<Customer>(Customer.class);
Customer query = queryRunner.query(connection, sql, customerBeanHandler, 2);

System.out.println(query);

} catch (Exception e) {
throw new RuntimeException(e);
} finally {
DbUtils.close(connection);
}
}

/**
* BeanListHandler:查询 Customers 中的多条数据
*/
@Test
public void testQueryInfos() {
Connection connection = null;
try {
connection = JDBCUtils.getConnection();

QueryRunner queryRunner = new QueryRunner();

String sql = "select id,name,email,birth from customers where id <= ?";
BeanListHandler<Customer> customerBeanHandler = new BeanListHandler<Customer>(Customer.class);
List<Customer> customers = queryRunner.query(connection, sql, customerBeanHandler, 10);

customers.forEach(System.out::println);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(null, connection, null);
}
}

/**
* MapHandler:是ResultSetHandler接口的实现类,对应表中的一条记录。
* 将字段及相应字段的值作为map中的key和value
*/
@Test
public void testReturnMap() {
Connection connection = null;
try {
QueryRunner runner = new QueryRunner();
connection = JDBCUtils.getConnection();
String sql = "select id,name,email,birth from customers where id = ?";
MapHandler handler = new MapHandler();
Map<String, Object> map = runner.query(connection, sql, handler, 10);
System.out.println(map);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(null, connection, null);
}
}

/**
* MapListHandler:是ResultSetHandler接口的实现类,对应表中的多条记录。
* 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
*/
@Test
public void testReturnMapList() {
Connection connection = null;
try {
QueryRunner runner = new QueryRunner();
connection = JDBCUtils.getConnection();
String sql = "select id,name,email,birth from customers where id < ?";

MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(connection, sql, handler, 13);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, connection, null);
}
}

/**
* ScalarHandler:用于查询特殊值
*/
@Test
public void testReturnScalarHandler() {
Connection conn = null;
try {

QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();

String sql = "select count(*) from customers";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);

System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn);
}
}

/**
* ScalarHandler:用于查询特殊值
*/
@Test
public void testReturnScalarHandler2() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();

String sql = "select max(birth) from customers";

ScalarHandler handler = new ScalarHandler();
Date maxBirth = (Date) runner.query(conn, sql, handler);
System.out.println(maxBirth);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn);
}
}

/**
* 自定义ResultSetHandler的实现类
*/
@Test
public void testQuery7() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();

String sql = "select id,name,email,birth from customers where id = ?";
ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {

@Override
public Customer handle(ResultSet rs) throws SQLException {
if (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
return customer;
}
return null;
}

};
Customer customer = runner.query(conn, sql, handler, 13);
System.out.println(customer);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn);
}
}
}

点击查看运行结果
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
520, 2023 10:57:57 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
2014-01-17
520, 2023 10:57:57 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-2} inited
Customer{id=2, name='王菲', email='wangf@163.com', birth=1988-12-26}
520, 2023 10:57:58 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-3} inited
Customer{id=1, name='汪峰', email='wf@126.com', birth=2010-02-02}
Customer{id=2, name='王菲', email='wangf@163.com', birth=1988-12-26}
Customer{id=3, name='林志玲', email='linzl@gmail.com', birth=1984-06-12}
Customer{id=4, name='汤唯', email='tangw@sina.com', birth=1986-06-13}
520, 2023 10:57:58 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-4} inited
{id=4, name=汤唯, email=tangw@sina.com, birth=1986-06-13}
520, 2023 10:57:58 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-5} inited
{id=1, name=汪峰, email=wf@126.com, birth=2010-02-02}
{id=2, name=王菲, email=wangf@163.com, birth=1988-12-26}
{id=3, name=林志玲, email=linzl@gmail.com, birth=1984-06-12}
520, 2023 10:57:58 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-6} inited
16
520, 2023 10:57:58 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-7} inited
添加了1条记录
520, 2023 10:57:59 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-8} inited
Customer{id=4, name='汤唯', email='tangw@sina.com', birth=1986-06-13}

Process finished with exit code 0

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
package Exer.bean;

import java.util.Date;

public class Customer {
private int id;
private String name;
private String email;
private Date birth;

public Customer() {
}

public Customer(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}

public int getId() {
return id;
}

public void setId(int 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 Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth=" + birth +
'}';
}
}

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 Exer.junit;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {


/**
* 获取 Connection 连接,使用数据库连接池(Druid)
*
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
return conn;
}


/**
* 关闭资源,但是 org.apache.commons.dbutils.DbUtils 有关闭资源的方法,所以下面方法可不用。
* @param connection
*/
public static void closeResource(Connection connection) {
//TODO 资源关闭
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}


/**
* 关闭资源,但是 org.apache.commons.dbutils.DbUtils 有关闭资源的方法,所以下面方法可不用。
*
* @param ps
* @param connection
* @param rs
*/
public static void closeResource(Statement ps, Connection connection, ResultSet rs) {
//TODO 资源关闭
try {
if (ps != null) {
ps.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}

try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}

try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

}