orm jdbc_Spring Data JDBC通用DAO实现–迄今为止最轻量的ORM

orm jdbc

我很高兴宣布Spring Data JDBC存储库项目的第一个版本。 这个开源库的目的是为基于Spring框架中 JdbcTemplate关系数据库提供通用,轻量且易于使用的DAO实现,与项目的Spring Data 框架兼容。

设计目标

  • 轻巧,快速且开销低。 只有少数几个类, 没有XML,注释,反射
  • 这不是成熟的ORM 。 没有关系处理,延迟加载,脏检查,缓存
  • 在几秒钟内实现CRUD
  • 对于JPA过大的小型应用程序
  • 在需要简单性或考虑将来迁移到JPA时使用
  • 对数据库方言差异的最小化支持(例如,结果的透明分页)

特征

每个DAO为以下内容提供内置支持:

  • 通过RowMapper抽象到域对象/从域对象映射
  • 生成的和用户定义的主键
  • 提取生成的密钥
  • 复合(多列)主键
  • 不变的领域对象
  • 分页(请求结果子集)
  • 按几列排序(与数据库无关)
  • 多对一关系的可选支持
  • 支持的数据库(不断测试):
    • MySQL
  • 通过SqlGenerator类可以轻松扩展到其他数据库方言。
  • 通过ID轻松检索记录

API

与Spring Data PagingAndSortingRepository抽象兼容, 所有这些方法都为您实现

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {T  save(T entity);Iterable<T> save(Iterable<? extends T> entities);T  findOne(ID id);boolean exists(ID id);Iterable<T> findAll();long count();void delete(ID id);void delete(T entity);void delete(Iterable<? extends T> entities);void deleteAll();Iterable<T> findAll(Sort sort);Page<T> findAll(Pageable pageable);
}

还完全支持PageableSort参数,这意味着您可以通过任意属性免费获得分页和排序 。 例如,假设您有userRepository扩展了PagingAndSortingRepository<User, String>接口(由库为您实现),并且在应用某种排序后,您请求了USERS表的第5页,每页10个:

Page<User> page = userRepository.findAll(new PageRequest(5, 10,new Sort(new Order(DESC, "reputation"),new Order(ASC, "user_name")))
);

Spring Data JDBC存储库库将把此调用转换为(PostgreSQL语法):

SELECT *
FROM USERS
ORDER BY reputation DESC, user_name ASC
LIMIT 50 OFFSET 10

…甚至(Derby语法):

SELECT * FROM (SELECT ROW_NUMBER() OVER () AS ROW_NUM, t.*FROM (SELECT *FROM USERSORDER BY reputation DESC, user_name ASC) AS t) AS a
WHERE ROW_NUM BETWEEN 51 AND 60

无论使用哪个数据库,都将获得Page<User>对象作为返回对象(您仍然必须自己提供RowMapper<User>才能将其从ResultSet转换为域对象。如果您还不知道Spring Data项目,那么Page<T>是一个很棒的抽象,不仅封装了List<User> ,而且还提供了元数据,例如记录总数,我们当前所在的页面等。

使用理由

  • 由于将来您的代码将仅依赖于Spring Data Commons伞项目中的PagingAndSortingRepositoryCrudRepository定义的方法, PagingAndSortingRepository您可以自由地从JdbcRepository实现(从该项目)切换到: JpaRepositoryMongoRepositoryGemfireRepositoryGraphRepository 。 它们都实现相同的通用API。 当然,不要指望从JDBC切换到JPA或MongoDB就像切换导入的JAR依赖项一样简单-但是至少您可以通过使用相同的DAO API最小化影响。
  • 您需要一个快速,简单的JDBC包装器库。 JPA甚至MyBatis都不过分
  • 如果需要,您想完全控制生成SQL
  • 您想使用对象,但是不需要延迟加载,关系处理,多级缓存,脏检查……您需要CRUD等等
  • 您想干吗
  • 您已经在使用Spring或什至JdbcTemplate ,但仍然觉得手工工作过多
  • 您的数据库表很少

入门

有关更多示例和工作代码,请不要忘记检查项目测试 。

先决条件

Maven坐标:

<dependency><groupId>com.blogspot.nurkiewicz</groupId><artifactId>jdbcrepository</artifactId><version>0.1</version>
</dependency>

不幸的是,该项目尚未在Maven中央存储库中 。 目前,您可以通过克隆将库安装在本地存储库中:

$ git clone git://github.com/nurkiewicz/spring-data-jdbc-repository.git
$ git checkout 0.1
$ mvn javadoc:jar source:jar install

为了启动您的项目,必须存在DataSource bean并启用事务管理。 这是一个最小MySQL配置:

@EnableTransactionManagement
@Configuration
public class MinimalConfig {@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}@Beanpublic DataSource dataSource() {MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();ds.setUser("user");ds.setPassword("secret");ds.setDatabaseName("db_name");return ds;}}

具有自动生成的密钥的实体

假设您有一个具有自动生成的密钥(MySQL语法)的以下数据库表:

CREATE TABLE COMMENTS (id INT AUTO_INCREMENT,user_name varchar(256),contents varchar(1000),created_time TIMESTAMP NOT NULL,PRIMARY KEY (id)
);

首先,您需要创建到该表的域对象User映射(就像在任何其他ORM中一样):

public class Comment implements Persistable<Integer> {private Integer id;private String userName;private String contents;private Date createdTime;@Overridepublic Integer getId() {return id;}@Overridepublic boolean isNew() {return id == null;}//getters/setters/constructors/...
}

除了标准的Java样板之外,您还应该注意实现Persistable<Integer> ,其中Integer是主键的类型。 Persistable<T>是一个来自Spring Data项目的接口,这是我们对您的域对象的唯一要求。

最后,我们准备创建CommentRepository DAO:

@Repository
public class CommentRepository extends JdbcRepository<Comment, Integer> {public CommentRepository() {super(ROW_MAPPER, ROW_UNMAPPER, "COMMENTS");}public static final RowMapper<Comment> ROW_MAPPER = //see belowprivate static final RowUnmapper<Comment> ROW_UNMAPPER = //see below@Overrideprotected Comment postCreate(Comment entity, Number generatedId) {entity.setId(generatedId.intValue());return entity;}
}

首先,我们使用@Repository批注标记DAO bean。 它启用持久性异常转换。 通过CLASSPATH扫描也可以找到这种带注释的bean。

如您所见,我们扩展了JdbcRepository<Comment, Integer> (该库的中心类),它提供了所有PagingAndSortingRepository方法的实现。 它的构造函数具有三个必需的依赖项: RowMapperRowUnmapper和表名。 您也可以提供ID列名称,否则使用默认的"id"

如果您曾经使用过Spring的JdbcTemplate ,则应该熟悉RowMapper界面。 我们需要以某种方式将ResultSet列提取到一个对象中。 毕竟,我们不想使用原始的JDBC结果。 这很简单:

public static final RowMapper<Comment> ROW_MAPPER = new RowMapper<Comment>() {@Overridepublic Comment mapRow(ResultSet rs, int rowNum) throws SQLException {return new Comment(rs.getInt("id"),rs.getString("user_name"),rs.getString("contents"),rs.getTimestamp("created_time"));}
};

RowUnmapper来自此库,它本质上与RowMapper相反:接收一个对象并将其转换为Map 。 库稍后使用此映射来构造SQL CREATE / UPDATE查询:

private static final RowUnmapper<Comment> ROW_UNMAPPER = new RowUnmapper<Comment>() {@Overridepublic Map<String, Object> mapColumns(Comment comment) {Map<String, Object> mapping = new LinkedHashMap<String, Object>();mapping.put("id", comment.getId());mapping.put("user_name", comment.getUserName());mapping.put("contents", comment.getContents());mapping.put("created_time", new java.sql.Timestamp(comment.getCreatedTime().getTime()));return mapping;}
};

如果您从不更新数据库表(仅读取插入在其他位置的一些参考数据),则可以跳过RowUnmapper参数或使用MissingRowUnmapper

最后一个难题是postCreate()回调方法,该方法在插入对象后调用。 您可以使用它来检索生成的主键并更新域对象(如果域对象是不可变的,则返回新的主键)。 如果不需要它,就不要重写postCreate() 。 根据此示例,检查JdbcRepositoryGeneratedKeyTest以获取有效的代码。

到目前为止,您可能会觉得与JPA或Hibernate相比,有很多手动工作。 但是,众所周知,各种JPA实现和其他ORM框架都会引入大量开销并显示一些学习曲线。 这个微小的库有意让用户承担一些责任,以避免复杂的映射,反射,注释……所有并非总是需要的隐式性。 该项目无意替代成熟稳定的ORM框架。 相反,它试图填补原始JDBC和ORM之间的利基,其中简单性和低开销是关键特征。

具有手动分配的密钥的实体

在此示例中,我们将看到如何处理具有用户定义的主键的实体。 让我们从数据库模型开始:

CREATE TABLE USERS (user_name varchar(255),date_of_birth TIMESTAMP NOT NULL,enabled BIT(1) NOT NULL,PRIMARY KEY (user_name)
);

…和User域模型:

public class User implements Persistable<String> {private transient boolean persisted;private String userName;private Date dateOfBirth;private boolean enabled;@Overridepublic String getId() {return userName;}@Overridepublic boolean isNew() {return !persisted;}public User withPersisted(boolean persisted) {this.persisted = persisted;return this;}//getters/setters/constructors/...}

注意,添加了特殊的persisted瞬态标志。 来自Spring Data项目的CrudRepository.save()合同要求一个实体知道其是否已保存( isNew() )方法–没有单独的create()update()方法。 对于自动生成的键,实现isNew()很简单(请参见上面的Comment ),但是在这种情况下,我们需要一个额外的瞬态字段。 如果您讨厌这种解决方法,并且只插入数据而从不更新,则始终可以从isNew()返回true

最后是我们的DAO, UserRepository bean:

@Repository
public class UserRepository extends JdbcRepository<User, String> {public UserRepository() {super(ROW_MAPPER, ROW_UNMAPPER, "USERS", "user_name");}public static final RowMapper<User> ROW_MAPPER = //...public static final RowUnmapper<User> ROW_UNMAPPER = //...@Overrideprotected User postUpdate(User entity) {return entity.withPersisted(true);}@Overrideprotected User postCreate(User entity, Number generatedId) {return entity.withPersisted(true);}
}

"USERS""user_name"参数指定表名和主键列名。 我将保留mapper和unmapper的详细信息(请参阅源代码 )。 但是请注意postUpdate()postCreate()方法。 它们确保一旦对象被持久保存,就设置了persisted标志,以便随后对save()调用将更新现有实体,而不是尝试重新插入它。

根据此示例,检查JdbcRepositoryManualKeyTest以获取有效的代码。

复合主键

我们还支持复合主键(由几列组成的主键)。 以该表为例:

CREATE TABLE BOARDING_PASS (flight_no VARCHAR(8) NOT NULL,seq_no INT NOT NULL,passenger VARCHAR(1000),seat CHAR(3),PRIMARY KEY (flight_no, seq_no)
);

我希望您注意到Peristable<T>的主键类型:

public class BoardingPass implements Persistable<Object[]> {private transient boolean persisted;private String flightNo;private int seqNo;private String passenger;private String seat;@Overridepublic Object[] getId() {return pk(flightNo, seqNo);}@Overridepublic boolean isNew() {return !persisted;}//getters/setters/constructors/...}

不幸的是,我们不支持将所有ID值封装在一个对象中的小数值类(就像JPA使用@IdClass ),因此您必须使用Object[]数组。 定义DAO类类似于我们已经看到的内容:

public class BoardingPassRepository extends JdbcRepository<BoardingPass, Object[]> {public BoardingPassRepository() {this("BOARDING_PASS");}public BoardingPassRepository(String tableName) {super(MAPPER, UNMAPPER, new TableDescription(tableName, null, "flight_no", "seq_no"));}public static final RowMapper<BoardingPass> ROW_MAPPER = //...public static final RowUnmapper<BoardingPass> UNMAPPER = //...}

需要注意的两件事:我们扩展了JdbcRepository<BoardingPass, Object[]>并且按预期提供了两个ID列名称: "flight_no", "seq_no" 。 我们通过提供由Object[]包裹的flight_noseq_no (必须seq_no顺序)值来查询此类DAO:

BoardingPass pass = repository.findOne(new Object[] {"FOO-1022", 42});

毫无疑问,这在实践中很麻烦,因此我们提供了微小的辅助方法,您可以静态导入:

import static com.blogspot.nurkiewicz.jdbcrepository.JdbcRepository.pk;
//...BoardingPass foundFlight = repository.findOne(pk("FOO-1022", 42));

根据此示例,检查JdbcRepositoryCompoundPkTest以获取工作代码。

交易次数

该库与事务管理完全正交。 每个存储库的每种方法都需要运行事务,并且要由您来设置它。 通常,您将@Transactional放在服务层上(称为DAO bean)。 我不建议将@Transactional放在每个DAO bean上 。

快取

Spring Data JDBC存储库库不提供任何缓存抽象或支持。 但是, 在Spring中使用缓存抽象将@Cacheable层添加@Cacheable DAO或服务之上非常简单。 另请参见: Spring中的@Cacheable开销

会费

..总是欢迎。 不要犹豫, 提交错误报告并提出请求 。 现在最大的缺失功能是对MSSQL和Oracle数据库的支持。 如果有人可以看一下,那将是非常棒的。

测试中

该库已使用Travis( 建立状态 )。 测试套件包含265个测试 (53个不同的测试,每个测试针对5个不同的数据库运行:MySQL,PostgreSQL,H2,HSQLDB和Derby。

在填写错误报告或提交新功能时,请尝试包括支持测试用例。 每个拉取请求都会在单独的分支上自动进行测试。

建造

分叉后, 正式的存储库构建就像运行一样简单:

$ mvn install

在JUnit测试执行期间,您会注意到很多异常。 这是正常的。 一些测试是针对仅在Travis CI服务器上可用MySQL和PostgreSQL运行的。 当这些数据库服务器不可用时,只需跳过整个测试:
结果:

Tests run: 265, Failures: 0, Errors: 0, Skipped: 106

异常堆栈跟踪来自根AbstractIntegrationTest

设计

库仅包含少数几个类,如下图所示:

UML图

JdbcRepository是实现所有PagingAndSortingRepository方法的最重要的类。 每个用户存储库都必须扩展此类。 同样,每个此类存储库都必须至少实现RowMapperRowUnmapper (仅当您要修改表数据时)。

SQL生成委托给SqlGeneratorPostgreSqlGenerator.DerbySqlGenerator用于与标准生成器不DerbySqlGenerator的数据库。

执照

该项目是在Apache许可的 2.0版(与Spring框架相同)下发布的。

参考: NoBlogDefFound博客中的JCG合作伙伴 Tomasz Nurkiewicz 为程序员提供的概率分布 。

翻译自: https://www.javacodegeeks.com/2013/01/spring-data-jdbc-generic-dao-implementation-most-lightweight-orm-ever.html

orm jdbc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NOIP模拟测试9「随·单·题」

liu_runda出的题&#xff0c;先$\%\%\%\%\%\%\%\%\%\%\%$为敬 随 考试时没有Qj 然后甚至没做,甚至没交 我不知道我怎么想的 这个题挺难改 你需要用到 循环矩阵快速幂,矩阵快速幂优化,打表找规律的基础 题解 首先我们可以列出来一个普通的dp式子 设f为第i次操作,操作后x变为j的概…

es5直接引入html文件,ES6+转ES5(webpack+babel、指定多个js文件、自动注入)

接续上篇ES6转ES5&#xff0c;本篇将使用webpack和babel将多个不同目录下指定的多个ES6语法的js文件编译为ES5&#xff0c;并将编译后的文件配置注入对应的html文件。一、新建项目&#xff0c;目录如下二、执行命令初始化项目cnpm init -y执行成功后会生成文件&#xff1a;pack…

在Spring MVC中,InternalResourceViewResolver做什么?

InternalResourceViewResolver是Spring MVC框架中ViewResolver一个实现&#xff0c;它将逻辑视图名称&#xff08;例如“ hello”&#xff09;解析为内部物理资源&#xff08;例如Servlet和JSP文件&#xff0c;例如放置在WEB-INF文件夹下的jsp文件&#xff09;。 它是UrlBasedV…

深圳敏捷云计算机科技,敏捷云 | 关于我们 | 敏捷云

(93) Afghanistan(355) Albania(213) Algeria(1) American Samoa(376) Andorra(244) Angola(1) Anguilla(1) Antigua(54) Argentina(374) Armenia(297) Aruba(61) 澳大利亚(43) Austria(994) Azerbaijan(973) Bahrain(880) Bangladesh(1) Barbados(375) Belarus(32) Belgium(50…

算法:用户喜好--Map与List配合下的查找

提示&#xff1a;在算法处理过程中&#xff0c;未必就要将出现在前面的作为关键字检索。比如本题&#xff0c;非得先去检索范围&#xff0c;再去判断范围中key的个数。反其道而行&#xff0c;把输入的数字当作关键字&#xff0c;组成Map package test;import java.util.ArrayLi…

带有Gradle的Spring Boot Web应用程序

1.简介 在继续使用Gradle创建演示Spring Boot Web应用程序之前&#xff0c;我假设我们已经准备好使用Gradle设置 。 摇篮设置指南 Gradle官方网站 2.创建演示应用程序 现在我们已经准备好安装插件&#xff0c;创建一个新的Gradle项目&#xff0c;如下所示– 单击下一步 &am…

html定义变量的语句规则,前端规范

前言&#xff1a;不规范的开发不仅使日后代码维护变的困难&#xff0c;同时也不利于团队的合作。下面列出了前端开发规范通用规范&#xff1a;1、前端工具统一sublime text&#xff0c;配置信息统一&#xff0c;如下&#xff1a;{"caret_style": "phase",&…

云技术-SaaS架构初步理解

最近公司准备整一个SaaS的东西。有幸参入这一块东西的搭建&#xff0c;借着這个机会也重新好好梳理了一下对SaaS的认识。今天整理一下&#xff01; 一、云计算与SaaS 说起SaaS&#xff0c;就得先说说云计算了。关于云计算分为三层&#xff0c;基础设施在最下端&#xff0c;平台…

primefaces_轻量级Web应用程序框架:PrimeFaces(JSF)+ Guice + MyBatis(第2部分)

primefaces在这一部分中&#xff0c;我将继续演示JSF&#xff0c;Guice和MyBatis的集成。 在持久层中使用DBCP连接池和MYSQL数据库。 看一下第1部分 。 在上一篇文章中 &#xff0c;我们创建了一个ServletContextListener。 现在&#xff0c;我们只需要在contextInitialized方…

html三元运算符 模板,AngularJS模板中的三元运算符

小编典典更新 &#xff1a;Angular1.1.5添加了一个三元运算符&#xff0c;因此现在我们可以简单地编写如果您使用的是较早版本的Angular&#xff0c;则有两个选择&#xff1a;(condition && result_if_true || !condition && result_if_false){true: result_if…

pycharm 更改创建文件默认路径

pycharm 更改创建文件默认路径 1、操作 依次找到以下路径修改为自己想要的路径即可&#xff1a;PyCharm——>Settings——>Appearance&Behavior——>System Setting——>Project Opening——>Default directory 2、图示 posted on 2019-07-29 14:18 初妍 阅…

Spring Security和多个过滤器链

Spring Security是一项非常有用的技术。 它使您可以保护应用程序而不会过于侵入&#xff0c;并允许插入许多不同的身份验证机制。 另一方面&#xff0c;要使用它并不是那么容易&#xff0c;并且每次接触它时我都必须重新学习这些工具之一。 在这篇文章中&#xff0c;我将介绍Sp…

计算机网络相关论文目录怎么弄,如何给你的标书、论文编页码和目录-论文页码设置...

一、如何从任意一页开始编码无论是做标书&#xff0c;还是写论文&#xff0c;都有一个给word文档编页码的问题。一般封面、目录、内容提要等前几页不要页码&#xff0c;页码从正文开始编起&#xff1b;或者前面内容编不同于正文的页码&#xff0c;或其中的某一页用罗马数字的页…

关于我

我的博客一般没有密码,有的话密码也是:znsbc, 写博客很少放题干,既然你搜到这个题解那么你肯定知道题干再搜的这个题 另外由于博主过于弱,所以博客难题会很少 借用$lnc$一句话&#xff08; WA0的快感&#xff0c;让我难以自拔。&#xff09; 另外由于博主写博客时大多神志不清…

NOIP模拟测试10「大佬·辣鸡·模板」

大佬 显然假期望 我奇思妙想出了一个式子$f[i]f[i-1]\sum\limits_{j1}^{j<m} C_{k \times j}^{k}\times w[j]$ 然后一想不对得容斥 于是我得到$f[i]f[i-1]\sum\limits_{j1}^{j<m} C_{j\times(k-1)}^{k-1} \times w[j]$ 但还是不对 现在思考第一个式子为什么不对 我们枚举…

activemq 持久订阅_ActiveMQ群集,持久订阅者和虚拟主题可助您一臂之力

activemq 持久订阅因此&#xff0c;您希望使用ActiveMQ跨分布式主题进行发布-订阅&#xff0c;并且要可靠。 您可以使用永久订阅&#xff0c;对吗&#xff1f; 可以&#xff0c;但是&#xff0c;如果您将群集与ActiveMQ一起使用&#xff0c;则可能会遇到意外的行为。 我最近在一…

会计电算化算不算计算机专业,成都会计学校会计电算化专业介绍

由于会计的火热&#xff0c;有很多同学都在咨询老师想要了解成都会计学校会计电算化专业&#xff0c;老师也整理了相关内容为同学们作为参考&#xff0c;希望对同学们有所帮助。就是将计算机信息技术应用到会计实务中&#xff0c;实现对会计信息的管理。会计电算化从20世纪50年…

给APK签名,修改签名

简介&#xff1a; 本来是不需要签名的&#xff0c;但是我有个android的专用设备&#xff0c;限制安装&#xff0c;但是售后给我通过了一个证书的验证。 那么我想装什么软件&#xff0c;就需要这个证书验证。 现在记录一下签名的主要过程&#xff0c;还有修改签名的方法。 一&am…

Neo4j:Cypher –删除重复的节点

我最近在处理的图上遇到问题&#xff0c;因为我没有应用任何唯一的约束 &#xff0c;我设法创建了重复的节点。 我想删除重复项&#xff0c;并遇到了吉米鲁茨&#xff08;Jimmy Ruts&#xff09;的精彩文章 &#xff0c;其中显示了一些方法。 让我们首先创建一个包含一些重复…

计算机学英文是什么,“计算机科学与技术专业本科课程”的英文翻译是什么?...

1计算机导论 Intorduction of Computer2高等数学 Avanced Mathematics3线性代数 Linear Alberia4离散数学 Discrete Mathematics5数值分析 Numerical value Analysis6大学英语 Colleage English7模拟电子电路 Analog Electronic Circuit8数字电子电路 Digital Electronic Circu…