0%

我们在 Spring Boot 项目中通过 Druid Spring Boot Starter 来集成 Druid 数据库连接池和监控。

加入依赖

添加 druid-spring-boot-starter maven 依赖。

截止 Nov, 2019, 最新版本是 1.1.21。

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>

添加配置

在 application.yml 配置文件里面添加如下必填信息。注:H2 内存数据库啥也不填都可以。

1
2
3
4
spring.datasource.druid.url= # 或spring.datasource.url= 
spring.datasource.druid.username= # 或spring.datasource.username=
spring.datasource.druid.password= # 或spring.datasource.password=
spring.datasource.druid.driver-class-name= #或 spring.datasource.driver-class-name=
1
2
3
4
5
6
7
8
9
10
11
12
spring:
# jdbc配置
datasource:
url: jdbc:mysql://localhost:3306/springboot-mybatis-plus?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver

druid:
initial-size: 5
max-active: 5
min-idle: 5

如果使用 MySQL,还需要添加 MySQL 驱动依赖。

1
2
3
4
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

不然就会报错 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

还有添加 jdbc starter。

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<artifactId>HikariCP</artifactId>
<groupId>com.zaxxer</groupId>
</exclusion>
</exclusions>
</dependency>

不然就会报错:java.lang.ClassNotFoundException: org.springframework.dao.DataAccessException

当然,如果使用 mybatis plus 这类框架,可以直接添加 mybatis-plus-boot-starter,它也会依赖 jdbc 的。

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
<exclusions>
<exclusion>
<artifactId>HikariCP</artifactId>
<groupId>com.zaxxer</groupId>
</exclusion>
</exclusions>
</dependency>

值得注意的是,默认会添加 HikariCP 的依赖,我们使用了 Druid 后,需要把 HikariCP 排除在外。

最后,我们可以打印看看 dataSource:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
CreateTime:"2020-04-06 15:04:34",
ActiveCount:0,
PoolingCount:5,
CreateCount:5,
DestroyCount:0,
CloseCount:0,
ConnectCount:0,
Connections:[
{ID:1011276990, ConnectTime:"2020-04-06 15:04:34", UseCount:0, LastActiveTime:"2020-04-06 15:04:34"},
{ID:1750563752, ConnectTime:"2020-04-06 15:04:34", UseCount:0, LastActiveTime:"2020-04-06 15:04:34"},
{ID:285781448, ConnectTime:"2020-04-06 15:04:34", UseCount:0, LastActiveTime:"2020-04-06 15:04:34"},
{ID:145329976, ConnectTime:"2020-04-06 15:04:34", UseCount:0, LastActiveTime:"2020-04-06 15:04:34"},
{ID:33558975, ConnectTime:"2020-04-06 15:04:34", UseCount:0, LastActiveTime:"2020-04-06 15:04:34"}
]
}

看如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;

@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}

@Override
public void invokeInsertThenRollback() throws RollbackException {
insertThenRollback();
}
}

方法 invokeInsertThenRollback() 调用一个有 @Transactional 注释的方法,这时,事务未生效。

有两种方法可以解决。

方法一,把自身 FooService 注入进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;

@Autowired
private FooService fooService;

@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}

@Override
public void invokeInsertThenRollback() throws RollbackException {
// insertThenRollback();
fooService.insertThenRollback();
}
}

方法二,调用 AopContext.currentProxy() 获取代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;

@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}

@Override
public void invokeInsertThenRollback() throws RollbackException {
// insertThenRollback();
((FooService) (AopContext.currentProxy())).invokeInsertThenRollback();
}
}

Python 函数具有非常多的参数形态,刚开始接触时很多人不一定了解所有参数的含义。

位置参数

这个很简单吧,跟其他语言都是一样的。

1
2
3
4
5
6
7
8
9
# 可以设置多个位置参数
def stock_information(code, name):
print('code:', code)
print('name:', name)

stock_information('000001.SZ', '平安银行')

code: 000001.SZ
name: 平安银行

默认参数

1
2
3
4
5
6
7
8
9
# 若不知道股票名称时,可以将股票名默认为“股票”
def stock_information(code, name='股票'):
print('code:', code)
print('name:', name)

stock_information('000001.SZ')

code: 000001.SZ
name: 股票

可变参数

可变参数可以是0个到任意个,调用函数时,会自动将可变参数组装成元组。可变参数的形式为 *args

1
2
3
4
5
6
7
8
9
10
11
# 若不知道要存几天的收盘价
def stock_information(code, name='股票', *args):
print('code:', code)
print('name:', name)
print('others:', args)

stock_information('000001.SZ', '平安银行', '1987-12-22', '银行')

code: 000001.SZ
name: 平安银行
others: ('1987-12-22', '银行')

这里需要注意的是,如果是赋值语句:

1
2
3
4
code, *list = '000001.SZ', '平安银行', '1987-12-22', '银行'
print(type(list))

<class 'list'>

这时,list就是一个list类型的变量了。

关键字参数

和上面不同的是,调用函数时,会自动将关键字参数组装成字典。可变参数的形式为 **kws

1
2
3
4
5
6
7
8
9
10
11
# 想要传入其他别的信息,比如公司成立日期、所属行业
def stock_information(code, name ='股票', **kws):
print('code:', code)
print('name:', name)
print('others:', kws)

stock_information('000001.SZ','平安银行', date='1987-12-22', industry='银行' )

code: 000001.SZ
name: 平安银行
others: {'date': '1987-12-22', 'industry': '银行'}

命名关键字参数

用户想要输入的关键字参数,在关键字参数前加分隔符 * ,即*, nkw,命名关键字不能缺少参数名。Python2貌似是不支持的。

1
2
3
4
5
6
7
8
9
10
def stock_information(code, name='股票', *, industry):
print('code:', code)
print('name:', name)
print('industry:', industry)

stock_information('000001.SZ', '平安银行', industry='银行' )

code: 000001.SZ
name: 平安银行
industry: 银行

参数组合

5种参数中的4种可以组合在一起使用,但组合有顺序。

位置参数、默认参数、可变参数、关键字参数

位置参数、默认参数、命名关键字参数、关键字参数