一, spring 数据访问哲学
1, 为避免持久化的逻辑分散在程序的各个组件中, 数据访问的功能应到放到一个或多个专注于此的组件中, 一般称之为数据访问对象 (data access object,DAO).
2, 良好的的 Repository 应以接口的形式向外暴露出去, 服务对象通过接口访问 Repository 对象, 这样做可以使服务对象很方便的进行测试, 甚至可以创建 mock 来进行测试.
3, 数据访问层应该是与持久化技术无关的方式来进行访问的, 这样可以使得切换底层的持久层框架对程序的其他地方所带来的影响最小.
服务对象与 Repository 之间的关系, 服务对象不会处理数据访问, 而是将其委托给 Repository.
二, spring 的数据访问异常体系
在使用原生的 JDBC 访问数据时, 应用程序必须去捕获 JDBC 抛出的异常 SQLException, 而且大多数情况下, 异常不会告诉你哪里出了问题, 如何处理.
spring JDBC 提供的数据访问异常体系解决了这两个问题, spring 提供了很多数据访问异常, 分别描述了他们抛出时所对应的问题. spring 为读取和写入数据的几乎所有错误都提供了异常. 而且 spring 提供的异常均继承自 DataAccessException, 该异常为运行时异常, 所以在代码中不必强制去捕获.
这样做的好处是:
开发者可以从底层繁琐复杂的技术细节中解脱出来.
开发者可以选择自己感兴趣的异常进行处理 .
常见的 spring 异常
异常类 | 说明 |
CleanupFailureDataAccessException | 执行 DAO 操作成功,但在释放数据资源时发生异常,如关闭 Connection 时发生异常。 |
ConcurrencyFailureException | 并发地操作数据时发生异常,如无法获取乐观锁或悲观锁时、死锁引发的失败等场景。 |
DataAccessResourceFailureException | 访问数据资源失败,如无法获取数据连接,无法获取 Hibernate 的会话等场景。 |
DataRetrievalFailureException | 获取数据失败,如找不到对应主键的数据或使用了错误的列索引等场景。 |
DataSourceLookupFailureException | 无法从 JNDI 中查找到数据源。 |
DataIntegrityViolationException | 数据操作违反了数据一致性限制时抛出,如插入重复的主键或引用不存在的外键场景。 |
InvalidDataAccessApiUsageException | 不正确地调用某一种持久化技术时抛出,如在 Spring JDBC 中查询对象在调用前没有事先进行编译操作,就会抛出该异常。这种异常主要是因为不正确地使用持久化技术而产生的。 |
InvalidDataAccessResourceUsageException | 在访问数据源时使用了不正确的方法时抛出,如写错 SQL 语句。 |
PermissionDeniedDataAccessException | 数据访问权限不足时抛出。如仅拥有只读权限却试图更改数 |
UncategorizedDataAccessException | 其它未被分类的异常。 |
................ | ................. |
为了使用 spring 的数据访问异常, 必须使用 spring 支持的数据访问模板.
三, 数据访问模板
模板方法将过程中与特定实现相关的部分委托给接口, 接口的不同实现定义了过程中的具体行为.(设计模式中模板模式的案例? 没看过源码, 不确定是不是
spring 在进行数据访问过程中将固定的和可变的明确划分为两个不同的类: 模板和回调. 模板管理过程中固定的部分, 回调处理自定义的数据访问代码.
模板与回调的职责, 模板类处理固定部分: 事物控制, 管理资源以及异常处理, 回调处理语句, 绑定参数以及整理结果集.
如果直接使用 JDBC, 可以使用 JdbcTemplate, 若使用 ORM 框架, 可以考虑使用 JpaTemplate 或者 HibernateTemplate 等.
模板类 | 用途 |
jdbc.core.JdbcTemplate | JDBC 链接 |
jdbc.core.namedparam.NamedParameterJdbcTemplate | 支持命名参数的 JDBC 链接 |
orm.ibatis.SqlMapClientTemplate | IBATIS SqlMap 客户端 |
orm.Jpa.JpaTemplate | java 持久化 API 的实体管理器 |
............ | ........... |
spring 提供的数据访问模板
四, 数据源的配置
spring 多种数据源配置方式
JDBC 驱动程序定义的数据源
JNDI 查找的数据源
连接池的数据源
考虑到现在基本都是 spring boot 的天下了, 基本也不使用 xml 配置了, 故此仅给出 java 配置.
1,JNDI 数据源 (没用过仅做记录)
java 配置:
- @Bean
- public JndiObjectFactoryBean dataSource(){
- JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
- factoryBean.setJndiName("jdbc/myProjectDS");// 指定 JNDI 中资源的名称.
- factoryBean.setResourceRef(true);// 若运行在 java 服务器中需要设置为 true
- factoryBean.setProxyInterface(DataSource.class);
- return factoryBean;
- }
2, 数据源连接池
常见的数据库连接池有以下几个:
- Apache commons DBCP
- c3p0
- BoneCP
- Druid
java 配置:
- @Bean
- public BasicDataSource dataSource(){
- BasicDataSource ds = new BasicDataSource();
- ds.setDriverClassName("org.h2.Driver");
- ds.setUrl("jdbc:h2:tcp://localhost/~/test");
- ds.setUsername("root");
- ds.setPassword("123");
- ds.setInitialSize(5);
- ds.setMaxActive(10);
- return ds;
- }
3, 基于 JDBC 的驱动的数据源
这种数据源是最简单的配置方式, spring 提供了三个这样的数据源类
DriverManagerDataSource: 在每个连接请求时都会返回一个新建的链接
simpleDriverDataSOurce: 与 DriverManagerDataSource 类似, 不同的是他直接使用 JDBC 驱动
SingleConnectionDataSource: 在每个连接请求时都会返回同一个连接, 可认为是只有一个连接的池, 该类不适合多线程的应用程序.
4, 嵌入式数据源, H2
嵌入式数据源作为应用的一部分运行, 而不是独立的数据库服务器, 对生产环境没有啥用处, 但对开发和测试而言是非常好的方案
- @Bean
- public DataSource dataSource(){
- return new EmbeddedDatabaseBuilder()
- .setType(EmbeddedDatabaseType.H2)
- .addScript("classpath:schema.sql")
- .addScript("classpath:test-data.sql")
- .build();
- }
五, 使用 JDBC 模板
1, 首先需要配置一个 JDBCTemplate, 只需为期设置 DataSource 即可.
代码如下
- @Bean
- public JdbcTemplate jdbcTemplate(DataSource dataSource){
- return new JdbcTemplate(dataSource);
- }
2, 编写数据访问相关接口及实现类
图省事, 省去了接口类, 实际使用过程中自行添加.
UserRepository 类
- @Repository
- public class UserRepository {
- private JdbcOperations jdbcOperations;
- @Autowired
- public UserRepository(JdbcOperations jdbcOperations) {
- this.jdbcOperations = jdbcOperations;
- }
- public void addUser(User user){
- jdbcOperations.update("insert into user (username,password,email) values(?,?,?)",
- user.getUsername(),user.getPassword(),user.getEmail());
- }
- public User findUser(String id){
- return jdbcOperations.queryForObject("select * from user where id ="+id,(resultSet, i) -> User.builder()
- .username(resultSet.getString("username"))
- .password(resultSet.getString("password"))
- .email(resultSet.getString("email"))
- .id(resultSet.getLong("id"))
- .build());
- }
- }
UserController 类:
- @RestController
- public class UserController {
- private final UserRepository repository;
- @Autowired
- public UserController(UserRepository repository) {
- this.repository = repository;
- }
- @GetMapping("/addUser")
- public String aa(){
- repository.addUser(User.builder()
- .email("abc@aa.com")
- .username("tom")
- .password("123456")
- .build());
- return "succ";
- }
- @GetMapping("/query/{id}")
- public User queryUser(@PathVariable String id){
- return repository.findUser(id);
- }
- }
- JdbcOperations API(下次有空了总结哈):https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcOperations.html#execute-org.springframework.jdbc.core.StatementCallback-
Z, 参考资料
https://blog.csdn.net/deniro_li/article/details/82820966
来源: http://www.bubuko.com/infodetail-2965844.html