第二部分:Spring中的数据操作

05如何配置单数据源
Spring Boot 的配置演示
  • 引入对应数据库驱动–H2
  • 引入JDBC依赖–spring-boot-starter-jdbc
  • 获取DataSource Bean,打印信息
  • 也可通过/acturator/beans 查看Bean
实例代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBfHDkny-1627302945936)(5ABBCD602C5E4E82993BF03F5CA866B5)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzNdkx5b-1627302945937)(CEDD2775077340169CF12056E96C3682)]

@Slf4j //引入日志
implements CommandLineRunner // 实现顺序启动线程
// springboot 会默认注册一个H2 JDBC 在application中添加配置
management.endpoints.web.exposure.include=*  // 可在Web浏览器中访问
直接配置所需的Bean

数据源相关

  • DataSource(根据选择的连接池实现决定)

事务相关(可选)

  • PlatformTransactionManager(DataSourceTransactionManager)
  • TransactionTemplate

操作相关(可选)

  • JDBCTemplate
Spring Boot 做了哪些配置

DataSourceAutoConfiguration

  • 配置DataSource

DataSoureTransactionManagerAutoConfiguration

  • 配置DataSourceTransactionManager

JdbcTemplateAutoConfigurtion

  • 配置JdbcTemplate

符合条件才能配置

数据源相关配置

通用

  • spring.datasource.url=jdbc:mysql//localhost/test
  • spring.databsource.username=dbuser
  • spring.databsource.password-dbpass
  • spring.databsource.driver-class-name=com.mysql.jdbc.Driver(可选)

初始化内嵌数据库

  • spring.datasource.initalization-mode=embedded|always|never
  • spring.datasource.schema与spring.datasource.data确定初始化SQL文件
  • spring.datasource.platform=hsqldb|h2|oracle|mysql|postgresql(与前者对应)

初始化数据量

-- 创建schema.sql文件,会默认执行此脚本
create table foo(id int identity,bar varchar(64))
-- 创建data.sql文件,会默认插入此脚本数据
insert into foo

application.properties 设置彩色打印日志

spring.output.ansi.enabled=always

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tNqAtHCR-1627302945938)(78A11A53271749DCB05F828762D0CABA)]

06如何配置多数据源
配置多数据源的配置要分开
  • 有多个DataSource时系统如何判断
  • 对应的设施(事务、ORM等)如何选择DataSource
Spring Boot中的多数据源配置
手工配置两组DataSource及相关内容
与Spring Boot协同工作(二选一)
  • 配置@Primary类型的Bean
  • 排除Spring Boot的自动配置

DataSourceAutoConfiguration

DataSourceTransactionManagerAutoConfiguration

JdbcTemplateAutoConfiguration

Application.perproties配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4cQ8Zivv-1627302945940)(7F0705279C374D8CA874B54385E6BE9C)]

management.endpoints.web.exposure.include=*
spring.output.ansi.enabled=always

foo.datasource.url=jdbc:h2:men:foo
foo.datasource.username=sa
foo.datasource.password=

bar.datasource.url=jdbc:h2:men:bar
bar.datasource.username=sa
bar.datasource.password=
MultiDatasourceApplication 启动类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
		DataSourceTransactionManagerAutoConfiguration.class,
		JdbcTemplateAutoConfiguration.class})
@Slf4j
public class MultiDatasourceApplication {

	public static void main(String[] args) {
		SpringApplication.run(MultiDatasourceApplication.class, args);
	}

	@Bean
	@ConfigurationProperties("foo.datasource")
	public DataSourceProperties fooDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	public DataSource fooDataSource() {
		DataSourceProperties dataSourceProperties = fooDataSourceProperties();
		log.info("foo datasource: {}", fooDataSourceProperties().getUrl());
		return dataSourceProperties.initializeDataSourceBuilder().build();
	}

	@Bean
	@Resource
	public PlatformTransactionManager fooTxManager(DataSource fooDataSource) {
		return new DataSourceTransactionManager(fooDataSource);
	}

	@Bean
	@ConfigurationProperties("bar.datasource")
	public DataSourceProperties barDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	public DataSource barDataSource() {
		DataSourceProperties dataSourceProperties = barDataSourceProperties();
		log.info("foo datasource: {}", barDataSourceProperties().getUrl());
		return dataSourceProperties.initializeDataSourceBuilder().build();
	}

	@Bean
	@Resource
	public PlatformTransactionManager barTxManager(DataSource barDataSource) {
		return new DataSourceTransactionManager(barDataSource);
	}

}
07那些好用的连接池:HikariCP
HikariCP为什么快
  1. 字节码级别优化(很多方法通过JavaAssist)
  2. 大量小改进
  • 用FastStatementList代替ArrayList
  • 无锁集合ConncurrentBag
  • 代理类的优化(比如,⽤ invokestatic 代替了 invokevirtual)
Spring Boot中的配置
spring Boot 2.x
  • 默认使用HikariCp
  • 配置spring.datasource.hikari.* 配置
Spring Boot 1.x
  • 默认使用Tomcat连接池,需要移除tomcat-jdbc依赖
  • spring.datasource.type=com.zaxxxer.hikari.HikariDataSource
常用HikariCP配置参数
  • spring.datasource.hikari.maxmumPoolSize=10
  • spring.datasource.hikari.minimumldle=10
  • spring.datasource.hikari.idleTimeout=600000
  • spring.datasource.hikari.connectionTimout=30000
  • spring.datasource.hikari.maxLifrtime=1800000
其他配置详见HikariCP官网
  • https://github.com/brettwooldridge/HikariCP
07那些好用的连接池:HikariCP
HikariCP为什么快
  1. 字节码级别优化(很多方法通过JavaAssist)
  2. 大量小改进
  • 用FastStatementList代替ArrayList
  • 无锁集合ConncurrentBag
  • 代理类的优化(比如,⽤ invokestatic 代替了 invokevirtual)
Spring Boot中的配置
spring Boot 2.x
  • 默认使用HikariCp
  • 配置spring.datasource.hikari.* 配置
Spring Boot 1.x
  • 默认使用Tomcat连接池,需要移除tomcat-jdbc依赖
  • spring.datasource.type=com.zaxxxer.hikari.HikariDataSource
常用HikariCP配置参数
  • spring.datasource.hikari.maxmumPoolSize=10
  • spring.datasource.hikari.minimumldle=10
  • spring.datasource.hikari.idleTimeout=600000
  • spring.datasource.hikari.connectionTimout=30000
  • spring.datasource.hikari.maxLifrtime=1800000
其他配置详见HikariCP官网
  • https://github.com/brettwooldridge/HikariCP
08那些好⽤的连接池Alibaba Druid

“Durid连接池是阿里巴巴开源的数据库连接池项目。Druid连接池为监控而生,内置强大的监控功能,监控
特性不影响性能。功能强大,内置Logging能诊断Hack应用行为。” ——Alibaba Druid官方介绍

Durid
经过阿里巴巴大系统的考验,值得信赖
实用功能
  • 详细的监控(非常全面)
  • ExceptionSorter,针对主流数据库的返回码都有支持
  • SQL防注入
  • 内置加密配置
  • 众多扩展点,方便进行定制
数据源配置
直接配置DruidDataSource
通过druid-spring-boot-starter
  • spring.datasource.druid.*
spring.output.ansi.enabled=ALWAYS

spring.datasource.url=jdbc:h2:mem:foo
spring.datasource.username=sa
spring.datasource.password=

spring.datasource.druid.inital-size=5
spring-datasource.druid.max-active=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.filters=conn,config,stat,slf4j

spring.datasource.druid.connection-properties=config.decrypt=true;config.decrypt.key=${pulic-key}
spring.datasource.druid.filter.config.enabled=true

spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.test-on-return=true
spring.datasource.druid.test-while-idle=true
数据源配置
Filter 配置
  • spring.datasource.druid.filters=stat,config,wall,log4j (全部使用默认值)
密码加密
  • spring.datasource.password=<加密密码>
  • spring.datasource.druid.filter.config.enabled=true
  • spring.datasource.druid.connection-properties=config.decrypt=true;config.decrypt.key=
SQL防注入
  • spring.datasource.druid.filter.wall.enabled=true
  • spring.datasource.druid.filter.wall.db-type=h2
  • spring.datasource.druid.filter.wall.config.delete-allow=false
  • spring.datasource.druid.filger.wall.config.drop-table-allow=false
Druid Filter
  • 用于定制连接池操作的各种环节
  • 可以继承FilterEventAdapter
  • 修改META-INF/druid-filter.properties 增加Filter配置
public class connectionLogFilterEventAdapter{
    
    @Override
    public void connection_connecBefore(FilterChain chain,Properties info){
        log.info("BEFORE CONNECTION");
    }
    
    @Override
    public void connection_connecAfter(ConnectionProxy connection){
        log.info("AFTER connection!");
    }
连接池选择时的考虑点
  1. 可靠性
  2. 性能
  3. 功能
  4. 可运维性
  5. 可扩展性
  6. 其他
通过Spring JDBC 访问数据库
Spring的JDBC操作类
spring-jdbc
  • core、JdbcTemplate 等相关核心接口和类
  • datasource,数据源相关的辅助类
  • object,将基本的JDBC操作封装成对象
  • support,错误码等其他辅助工具
常见的Bean 注解
通过注解定义Bean
  • @Component
  • @Repository
  • @Service
  • @Controller
  • @RestController
简单的JDBC操作
JdbcTemplate
  • query
  • queryForObject
  • queryForList
  • update
  • execute
SQL批处理
JdbcTemplate
  • batchUpdate
  • BatchPreparedStatementSetter
NamedParamerterJdbcTemplate
  • batchUpdate
  • SqlParameterSourceUtile.createBatch
Project Code
  1. 创建数据库,创建schema.sql文件
CREATE TABLE FOO (ID INT IDENTITY, BAR VARCHAR(64));
  1. 初始化数据库,插入数据
INSERT INTO FOO (BAR) VALUES ('aaa');
  1. 创建类实体
@Data
@Builder
public class Foo {
    private Long id;
    private String bar;
}
  1. 创建Dao层,数据操作层
@Slf4j
@Repository
public class FooDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private SimpleJdbcInsert simpleJdbcInsert;

    public void insertData() {
        // asList作用:初始化数组
        Arrays.asList("b", "c").forEach(bar -> {
            jdbcTemplate.update("insert into FOO (BAR) VALUES (?)", bar);
        });

        HashMap<String, String> row = new HashMap<>();
        row.put("BAR", "d");
        Number id = simpleJdbcInsert.executeAndReturnKey(row);
        log.info("ID of d:{}", id.longValue());
    }

    public void listData() {
        log.info("count:{}", jdbcTemplate.queryForObject("select count(*) from foo", Long.class));
        List<String> list = jdbcTemplate.queryForList("select BAR from foo", String.class);
        list.forEach(s -> log.info("Bar :{}", s));
        List<Foo> fooList = jdbcTemplate.query("select * from foo", new RowMapper<Foo>() {

            @Override
            public Foo mapRow(ResultSet rs, int rowNum) throws SQLException {
                return Foo.builder()
                        .id(rs.getLong(1))
                        .bar(rs.getString(2))
                        .build();
            }
        });
        fooList.forEach(f -> log.info("Foo: {}", f));
    }

}
  1. 创建BatchDao层,批量操作数据Dao层
@Repository
public class BatchFooDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    public void batchUpdate() {
        jdbcTemplate.batchUpdate("INSERT INTO FOO(BAR) VALUES (?)", new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setString(1, "b-" + i);
            }

            @Override
            public int getBatchSize() {
                return 2;
            }
        });

        List<Foo> list = new ArrayList<>();
        list.add(Foo.builder().id(100L).bar("b-100").build());
        list.add(Foo.builder().id(101L).bar("b-101").build());
        namedParameterJdbcTemplate.batchUpdate("INSERT INTO FOO (ID,BAR) VALUES(:id,:bar)", SqlParameterSourceUtils.createBatch(list));
    }

}
  1. 调整主方法,Application
@SpringBootApplication
@Slf4j
public class SpringJdbcDemoApplication implements CommandLineRunner {

    @Autowired
    private FooDao fooDao;

    @Autowired
    private BatchFooDao batchFooDao;

    public static void main(String[] args) {
        SpringApplication.run(SpringJdbcDemoApplication.class, args);
    }

    @Bean
    @Autowired
    public SimpleJdbcInsert simpleJdbcInsert(JdbcTemplate jdbcTemplate) {
        return new SimpleJdbcInsert(jdbcTemplate).withTableName("FOO").usingGeneratedKeyColumns("ID");
    }

    @Bean
    @Autowired
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }

    @Override
    public void run(String... args) throws Exception {
        fooDao.insertData();
        batchFooDao.batchUpdate();
        fooDao.listData();

    }
}
10什么是Spring的事务抽象
Spring的事务抽象
一致的事务模型
  • JDBC/Hibernate/myBatis
  • DataSource/JTA
事务抽象的核心接口
  • PlatformTransactionManager
  • HibernateTransactionManager
  • jtaTranscationManager
TranscationDefinition
  • Proagation
  • lsolation
  • Timeout
  • Read only status
void commit(Transaction status) throws TranscationException;
void rollback(Transcation status) throws TranscationExeption;
TranscationStatus getTranscation(@Nullable TransactionDefinition definition) throws TransactionException;
事务传播特性
传播性描述
propagation_required0当前有事务就用当前的,没有就用新的
propagation_supports1事务可有可无,不是必须的
propagation_mandatory2当前一定要有事务,不然就抛异常
propagation_requires_new3无论是否有事务,都起个新的事务
propagation_not_supports4不支持事务,按非事务方式运行
propagation_nerver5不支持事务,如果有事务则抛异常
propagation_nested6当前有事务在当前事务里再起一个事务
事务隔离特性
隔离性脏读不可重复读幻读说明
isolation_read_uncommitted1未提交读
isolantion_read_commintted2×已提交读
isolantion_repeatable_read3××可重复读
isolantion_serialiable4×××串行化的
编程式事务
TransactionTemplate
  • TransactionCallback
  • TransactionCallbackWithoutResult
PlatformTransactionManager
  • 可以传入TransactionDefinition进行定义
  • HibernateTransactionManager
  • jtaTranscationManager
TranscationDefinition
  • Proagation
  • lsolation
  • Timeout
  • Read only status
void commit(Transaction status) throws TranscationException;
void rollback(Transcation status) throws TranscationExeption;
TranscationStatus getTranscation(@Nullable TransactionDefinition definition) throws TransactionException;
事务传播特性
传播性描述
propagation_required0当前有事务就用当前的,没有就用新的
propagation_supports1事务可有可无,不是必须的
propagation_mandatory2当前一定要有事务,不然就抛异常
propagation_requires_new3无论是否有事务,都起个新的事务
propagation_not_supports4不支持事务,按非事务方式运行
propagation_nerver5不支持事务,如果有事务则抛异常
propagation_nested6当前有事务在当前事务里再起一个事务
事务隔离特性
隔离性脏读不可重复读幻读说明
isolation_read_uncommitted1未提交读
isolantion_read_commintted2×已提交读
isolantion_repeatable_read3××可重复读
isolantion_serialiable4×××串行化的
11什么是Spring编程是事务(下)
编程式事务
TransactionTemplate
  • TransactionCallback
  • TransactionCallbackWithoutResult
PlatformTransactionManager
  • 可以传入TransactionDefinition进行定义
@SpringBootApplication
@Slf4j
public class ProgramaticTransactionDemoApplication implements CommandLineRunner{
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public static void main(String[] args){
        springApplication.run(ProgrammaticTransactionDemoApplication.class,args);
    }
    @Override
    public void run(String... args) throws Exception{
        log.info("COUNT BEFORE TRANSACTION:{}",getcount);
        transactionTemplate.execute(new TransactionCallbackWithResult(){
            @Override
            protected void doInTrancationWithoutResult(Transcation transcation){
                jdbcTemplate.execute("INSERT INTO FOO (ID,BAR) VALUES (1,'aaa')");
                log.into("COUNT IN TRANSACTION:{}",getCount());
                transactionStatus.setRollbackOnly();
            }
        });
        log.info("COUNT AFTER TRANSACTION: {}",getCount());
    }
    
    private long getCount(){
        return (long)jdbcTemplate.queryForList("SELECT COUNT(*) AS CNT FROM FOO").get(0).get(0).get("CNT")
    }
    
}
事务声明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cm6o0xIN-1630909447733)(https://note.youdao.com/yws/api/personal/file/2DF671A896504885A77D61CA7669D556?method=download&shareKey=e065241e048f5e55d6469b571eb359ca)]

基于注解的配置方式
开启事务注解的方式
  • @EnableTransactionManagement
  • tx:annotion-driven/
一些配置
  • proxyTargetClass
  • mode
  • order
@Transactional
  • transactionManager
  • propagation
  • isolation
  • timeout
  • readOnly
  • 怎么判断回滚
@Component
public class FooServiceImpl implements FooService{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Override
    @Transaction
    public void insertRecord(){
        jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");
    }
    
    @Override
    @Trancation(rollbackFor = RollbackException.class)
    public void insertThenRollback() throws RollbackException {
        jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES (BBB)");
        throw new RollbackException();
    }
    
    @Override
    public void invokeInsertThenRollback() throws RollbackExeception{
        insertThenRollback();
    }
}
了解SpringJDBC异常抽象
Spring的JDBC异常抽象

Spring会将数据操作的异常转换为DataAccessException
无论使用何种数据访问方式,都能使用一样的异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0lkTXqQL-1630909447736)(https://note.youdao.com/yws/api/personal/file/9FA8BCAF5B52479A87E6EBB9B406ABDC?method=download&shareKey=7128c73986e048b4f2d10f1d435eacaf)]

Spring是怎么认识那些错误码的
通过SQLErrorCodeSQLExceptionTranslator 解析错误码
ErrorCode 定义
  • org/springframework/jdbc/support/sql-error-codes.xml
  • Classpath 下的sql-error-codes.xml
定制错误码解析逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubmIHSXT-1630909447737)(https://note.youdao.com/yws/api/personal/file/709C889114F546FA82BC58022D5ADE24?method=download&shareKey=8d8c9a522fcbb23c507ca54bfe3d9744)]

12了解Spring的JDBC异常抽象
13课程答疑(上)
14课程答疑(下)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值