正在学习丁雪丰大大的Spring全家桶
如何快速的创建Spring boot项目?
可以通过start.spring.io创建Spring boot的骨架
选择项目、语言、版本、填写项目元数据内容、添加依赖
完成后,下载生成zip文件,导入项目即可
使用idea如下配置:
可能会提示连接失败,重试几次即可。
然后点下一步配置项目名与地址就可以直接在idea中打开
如何运行Spring Boot?
方法一
在SpringBootApplication中直接运行
方法二
通过maven命令导出jar包
mvn clean package -Dmaven.test.skip
java -jar ***.jar 执行jar包
第二种方式在生产、测试环境中运行比较方便。
创建单数据源
创建项目时选择数据库对应的依赖
配置properties,完成后,springboot会自动帮我们配置好dataSource、事务(transaction)等
也可以配置sql文件,项目启动后,自动建表以及添加数据。
创建多个数据源
两个数据源的参数配置要分开!
如何区分数据源?
第一种:在dataSource加上@Primary注解
那SpringBoot 就会在加了@Primary的bean中展开
分主次
第二种:如果没有主次之分的话
就排除一下SpringBoot的一些Bean的自动配置
DataSourceAutoConfiguration
DataSourceTransactionManagerAutoConfiguration
JdbcTemplateAutoConfiguration
然后自己在代码中控制哪个数据库对应哪个数据源
如何排除?
在注解@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class,JdbcTemplateAutoConfiguration.class})
然后自己就得开始配置,DataSource、TransactionManager等
如何配置?
1.首先配置application.properties,配置数据库参数(这边使用的是H2内存数据库)
foo.datasource.url=jdbc:h2:mem:foo
foo.datasource.username=sa
foo.datasource.password=
bar.datasource.url = jdbc:h2:mem:bar
bar.datasource.username = sa
bar.datasource.password =
通过foo.datasource和bar.datasource前缀区分不同的参数
那么,要如何区分呢?
需要创建一个返回DataSourceProperties的方法
通过注解:
@ConfigurationProperties(“foo.datasource”)
自动去Properties文件中寻找对应的值foo.datasource.url等,自动赋值到DataSourceProperties中
然后使用
DataSourceProperties.initializeDataSourceBuilder().build();
构建一个DataSource并返回
用@Bean注解,告诉Spring生成一个Bean对象,这个Bean对象Spring只会调用一次,然后将这个Bean放到IoC容器中。
最后添加事务
前面已经把DataSource配置好了,这时就可以使用构造函数注入方式,将dataSource注入到事务中
使用@Bean @Resource,将事务的Bean放到IoC容器中
@Bean
@Resource
public PlatformTransactionManager barTransactionManager(DataSource barDataSource){
return new DataSourceTransactionManager(barDataSource);
}
这样就配置好了多数据源的方式,根据项目方案,可自行配置多个数据源。
有哪些好用的连接池?
1.HikariCP(SpringBoot 2.x默认使用)
Hikari来源于日语,是“光”的意思,体现了HikariCP作者的起名意义,体现Hikari就是快!
It’s Faster
Hikari为什么会那么快?
1.字节码级别优化(很多方法通过JavaAssist生成)
2.大量小改进
1)使用FastStatementList替换ArrayList
2)无锁集合ConcurrentBag
3)代理类优化(比如:invokestatic代替了invodevirtual)
SpringBoot 1.x 默认使用Tomcat连接池,如果要使用HikariCP,需要移除Tomcat-jdbc依赖
再配置spring.datasource.type = com.zaxxer.hikari.HikariDataSource
在这边看一下Hikari数据源的源码
通过@ConditionalOnClass注解判断在classpath中有HikariDataSource这个类的时候,
并且@ConditionalOnMissingBean注解判断在Spring的上下文中,没有DataSource,最后用@ConditionalOnProperty判断在Properties中的spring.datasource.type的值是com.zaxxer.hikari.HikariDataSource的时候才调用Hikari数据源
然后通过@ConfigurationProperties注解获取属性前缀为spring.datasource.hikari,将参数都配置到DataSourceProperties中做初始化
常用的Hikari配置
spring.datasource.hikari.maximumPoolSize = 100
spring.datasource.hikari.minimumIdle = 10
spring.datasource.hikari.idleTimeout = 600000
spring.datasource.hikari.connectionTimeout = 80000
spring.datasource.hikari.maxLifetime = 1800000
其他详细配置:
https://github.com/brettwooldridge/HikariCP
2.Alibaba Druid
如果要使用Druid,那就最好将排除HikariCP
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<!-- 排除 HikariCP-->
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
Druid的优点有哪些呢?
1.Alibaba出品,经历过很多大系统的考验,安全可靠!
2.监控统计功能,很详细的监控
3.防SQL注入
4.内置密码加密
5.ExceptionSorter,针对主流数据库的返回码都有支持
(在Druid中,会根据连接池连接数据库的类型自动匹配不同类型的ExceptionSorter,不需要额外配置。这一点Druid和jboss是不同的。)
慢sql日志
系统属性配置
druid.stat.logShowSql = true
druid.stat.slowSqlMillis = 3000
String Boot
spring.datasource.druid.filter.stat.log-show-sql = true
spring.datasource.druid.filter.stat.slow-sql-millis = 300
如果有sql执行超过300ms,就报错
Druid Filter
用于定制连接池操作的各种环节
继承FilterEventAdapter,方便使用filter
配置META-INF的druid-filter.properties,用于增加filter配置
application.properties中配置Filter
spring.datasource.druid.filters=conn,config,stat,slf4j
其中conn就是自己增加的filter配置
这就需要配置druid-filter.properties
druid.filters.conn = com...ConnectionFilter
如何通过jdbc访问数据库?
可以通过JdbcTemplate进行访问
方法 | 描述 |
---|---|
queryForObject(sql,Class) | 查询数据库返回单个数据 |
queryForList(sql,Class) | 查询数据库,返回list |
query(sql,new RowMapper(Object){}) | 查询数据库,返回指定对象 |
update(sql,args) | 更新数据库,可进行update和insert操作 |
批量更新数据库有两种方法:
1.使用jdbcTemplate.batchUpdate
jdbcTemplate.batchUpdate("insert into default_tb(tname) values(?)", new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1,"d-"+i);
}
@Override
public int getBatchSize() {
return 2;
}
});
2.使用 namedParameterJdbcTemplate.batchUpdate
List<Foo> foos = new ArrayList<Foo>();
foos.add(Foo.builder().id(123).name("123").build());
namedParameterJdbcTemplate.batchUpdate(sql,SqlParameterSourceUtils.createBatch(foos))
若要获取查询数据后直接Id的话,可以使用SimpleJdbcInsert
但是需要写一下实现方法
@Bean
@Autowired
public SimpleJdbcInsert simpleJdbcInsert(JdbcTemplate jdbcTemplate){
//withTableName 关联的table
//usingGeneratedKeyColumns 使用的生成id字段
return new SimpleJdbcInsert(jdbcTemplate).withTableName("default_tb").usingGeneratedKeyColumns("id");
}
Map<String,String> values = new HashMap<String,String>();
values.put("tname","c");
Number key = simpleJdbcInsert.executeAndReturnKey(values);
log.info("c -> id = "+ key.intValue());
spring的事务抽象
事务有四大特征ACID
A:Atomicity(原子性)
原子的特性就是不可分割的,可以看做是组成物质的基本单位,也是我们对数据进行操作的基本单位。
C:Consistency(一致性)
无论事务提交或者回滚,对数据的完整性约束是不能改变的。即数据库进行事务操作后,会从一种一致性状态变为了另一种一致性状态。
I:Isolation(隔离性)
每个事务都是独立存在的,即每个事务提交前,对其他事务而言是不可见的。
D:Durability(持久性)
是通过事务日志进行保证的,日志包括了“回滚日志”、“重做日志”, 事务日志是“写前日志”,即在数据处理前,就将数据库变化的信息放入到重做日志中,这种方式保证了即使数据库宕机后,也能通过日志重新执行,从而保证了事务的持久性。
事务的传播特性
传播性 | 值 | 描述 |
---|---|---|
PROPAGATION_REQUIRED | 0 | 当前有事务就用当前的,没有事务的话就用新的 |
PROPAGATION_SUPPORTS | 1 | 事务可有可无,不是必须的 |
PROPAGATION_MANDATORY | 2 | 当前一定要有事务,不然就抛异常 |
PROPAGATION_REQUIRES_NEW | 3 | 无论是否有事务都启一个新的事务 |
PROPAGATION_NOT_SUPPORTED | 4 | 不支持事务,按非事务的方式运行 |
PROPAGATION_NEVER | 5 | 不支持事务,如果有事务就抛异常 |
PROPAGATION_NESTED | 6 | 如果有事务就在当前的事务内再启一个事务(内嵌事务) |
事务的隔离特性
隔离性 | 值 | 描述 |
---|---|---|
ISOLATION_READ_UNCOMMITTED | 1 | 隔离级别最低,一个事务可以读到另一个事务未提交的结果,所有并发问题都有可能发生。 |
ISOLATION_READ_COMMITTED | 2 | 可以解决脏读,但是不可重复读和幻读都存在 |
ISOLATION_REPEATABLE_READ | 3 | 可以解决不可重复读和脏读,但是幻读还是存在 |
ISOLATION_SERIALIZAABLE | 4 | 可以解决所有并发问题,但是效率低 |
脏读:事务B读取到了事务A未提交的数据,这时事务A提交失败了,事务B读取的数据就是脏数据。
不可重复读:使用同一条查询语句,前后两次查询的结果不一致。
幻读:同一条查询语句,前后两次查询集合的数量不一致。
若隔离级别越高,那么处理事务的效率越低。
这时因为,隔离级别越高,就越接近“串行化”,只有一个事务处理完,才会进行下一个事务的处理,这就肯定没有了并行的效率。
所以需要在正确性和性能之间做一个平衡。
编程式事务
TransactionTemplate
- TransactionRollback (有返回值的rollback)
- TransactionRollbackWithoutResult(没有返回值的rollback)
PlatformTransactionManager - 可以传入TransactionDefinition进行定义
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
jdbcTemplate.update("delete from foo");
log.info("after delete foo count->"+getCount());
transactionStatus.setRollbackOnly();
log.info("after rollback foo count->"+getCount());
}
});
声明式事务
基于注解的配置方式
开启事务的注解方式:
- @EnableTransactionManagement
- <tx-annotation-driven / >
能做以下配置:
- proxyTargetClass 代理
- mode
- order 事务AOP拦截的顺序,默认最低优先级
@Transactional
- propagation 配置传播特性
- isolation 配置隔离特性
- rollbackFor 配置遇到指定异常就回滚
- transactionManager
- readOnly
- timeout