- 前言
- 第一部分 基础应用开发
- 第 1 章 Spring Boot 入门
- 第 2 章 在 Spring Boot 中使用数据库
- 第 3 章 Spring Boot 界面设计
- 第 4 章 提高数据库访问性能
- 第 5 章 Spring Boot 安全设计
- 第二部分 分布式应用开发
- 第 6 章 Spring Boot SSO
- 第 7 章 使用分布式文件系统
- 第 8 章 云应用开发
- 第 9 章 构建高性能的服务平台
- 第三部分 核心技术源代码分析
- 第 10 章 Spring Boot 自动配置实现原理
- 第 11 章 Spring Boot 数据访问实现原理
- 第 12 章 微服务核心技术实现原理
- 附录 A 安装 Neo4j
- 附录 B 安装 MongoDB
- 附录 C 安装 Redis
- 附录 D 安装 RabbitMQ
- 结束语
11.2 数据存取功能实现原理
与数据库服务器建立连接之后,就可以对数据库执行一些存取操作,对数据库实现管理的功能。数据存取的操作大体上都包含两个方面的内容,即实体建模和持久化。不管是关系型数据库,还是 NoSQL 数据库,都遵循这一设计规范。
11.2.1 实体建模源代码分析
实体建模的原理简单地说,即将 Java 的普通对象和关系映射为数据库表及其相关的关系。而这种映射在 Spring Boot 中,主要是通过注解的方式来实现。几种数据库中主要的注解定义如表 11-1 所示。
表 11-1 实体建模主要注解定义
这种映射机制是双向的,当向数据库存入数据时,是将 Java 对象映射为数据库对象,而从数据库取出数据时,却将数据库中的数据还原为 Java 对象。
例如对于 Neo4j 来说,在实体建模中的主要注解 @NodeEntity 的定义如代码清单 11-6 所示。在一个类定义中使用这个注解,表示这个类定义就是一个节点实体的建模。
代码清单 11-6 @NodeEntity 源代码
package org.neo4j.ogm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited public @interface NodeEntity { String CLASS = "org.neo4j.ogm.annotation.NodeEntity"; String LABEL = "label"; String label() default ""; }
Neo4j 是一个图形数据库,所以程序中的实体对象要存入数据库时,将被映射为数据库图形。代码清单 11-7 是这种映射的部分实现代码。它的实现原理是,将实体对象转化为数据库可以识别的查询语句,实现对象到数据的转换。
代码清单 11-7 实体对象映射为数据库图形的部分源代码
package org.neo4j.ogm.mapper; ...... public class EntityGraphMapper implements EntityToGraphMapper { public CypherContext map(Object entity, int horizon) { if(entity == null) { throw new NullPointerException("Cannot map null object"); } else { SingleStatementCypherCompiler compiler = new SingleStatementCypher Compiler(); Iterator i$ = this.mappingContext.mappedRelationships().iterator(); while(i$.hasNext()) { MappedRelationship mappedRelationship = (MappedRelationship) i$.next(); this.logger.debug("context-init: (${})-[:{}]->(${})", new Object []{Long.valueOf(mappedRelationship.getStartNodeId()), mappedRelationship.getRelationshipType(), Long.valueOf(mappedRelationship.getEndNodeId())}); compiler.context().registerRelationship(mappedRelationship); } this.logger.debug("context initialised with {} relationships", Integer.valueOf(this.mappingContext.mappedRelationships().size())); if(this.isRelationshipEntity(entity)) { entity = this.entityAccessStrategy.getStartNodeReader(this.metaData.classInfo(entity)).read(entity); if(entity == null) { throw new RuntimeException("@StartNode of relationship entity may not be null"); } } this.mapEntity(entity, horizon, compiler); this.deleteObsoleteRelationships(compiler); return compiler.compile(); } } ...... }
当从 Neo4j 数据库中读取数据时,Neo4j 将数据库中的图形还原为实体对象。代码清单 11-8 是实现这种功能的部分源代码,即将从数据库中查询得到的数据集合转化为实体对象,实现从数据到对象的转换。
代码清单 11-8 数据库中图形还原为实体对象的部分源代码
package org.neo4j.ogm.mapper; ...... public class GraphEntityMapper implements GraphToEntityMapper<GraphModel> { private void mapNodes(GraphModel graphModel, List<Long> nodeIds) { Iterator i$ = graphModel.getNodes().iterator(); while(i$.hasNext()) { NodeModel node = (NodeModel)i$.next(); Object entity = this.mappingContext.getNodeEntity(node.getId()); try { if(entity == null) { entity = this.mappingContext.registerNodeEntity(this.entity Factory.newObject(node), node.getId()); } this.setIdentity(entity, node.getId()); this.setProperties(node, entity); this.mappingContext.remember(entity); nodeIds.add(node.getId()); } catch (BaseClassNotFoundException var7) { this.logger.debug(var7.getMessage()); } } } ...... }
11.2.2 持久化实现原理
关系型数据库都使用了 JPA 的一套执行标准,它结合使用 Hibernate 实现了实体的持久化。后续的数据库管理设计都遵循了 JPA 这一个标准规范,提供相同的访问数据库的 API。图 11-1 是 JPA、MongoDB、Neo4j 三种不同的资源库接口定义的相同的继承关系。
图 11-1 数据库资源库接口的继承关系
这就不难理解,为什么在 Spring Boot 中使用数据库,对于不同种类的数据库,几乎都可以使用相同的方法来访问。但是,上面不同数据库的资源库接口定义虽然有相同的继承关系,它们的实现方法却是不同的,JPA 由 SimpleJpaRepository 实现了 JpaRepository,如代码清单 11-9 所示。
代码清单 11-9 JPA 数据库持久化源代码片段
package org.springframework.data.jpa.repository.support; ...... @Repository @Transactional( readOnly = true ) public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> { private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!"; private final JpaEntityInformation<T, ?> entityInformation; private final EntityManager em; private final PersistenceProvider provider; private CrudMethodMetadata metadata; public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, Entity Manager entityManager) { Assert.notNull(entityInformation); Assert.notNull(entityManager); this.entityInformation = entityInformation; this.em = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); } …… public T findOne(ID id) { Assert.notNull(id, "The given id must not be null!"); Class domainType = this.getDomainClass(); if(this.metadata == null) { return this.em.find(domainType, id); } else { LockModeType type = this.metadata.getLockModeType(); Map hints = this.getQueryHints(); return type == null?this.em.find(domainType, id, hints):this.em. find(domainType, id, type, hints); } } ...... }
而对于 Neo4j 来说,它使用 GraphRepositoryImpl 实现了 GraphRepository,如代码清单 11-10 所示。
代码清单 11-10 Neo4j 数据库持久化源代码片段
package org.springframework.data.neo4j.repository; ...... @Repository public class GraphRepositoryImpl<T> implements GraphRepository<T> { private static final int DEFAULT_QUERY_DEPTH = 1; private final Class<T> clazz; private final Session session; public GraphRepositoryImpl(Class<T> clazz, Session session) { this.clazz = clazz; this.session = session; } ...... public T findOne(Long id) { return this.session.load(this.clazz, id); } ...... }
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论