jujube-jdbc

ORM 框架
授权协议 Apache
开发语言 Java
所属分类 程序开发、 ORM/持久层框架
软件类型 开源软件
地区 国产
投 递 者 唐渊
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

jujube-jdbc

一款简洁的ORM框架,融合了Mybatis和JPA的优势,简化了持久层的开发。

一、初衷

当我们出现邪恶的想法:自己造轮子,那么一定是现有软件的使用上遇到了不舒服的地方,不能满足你的胃口了。
那么在Java持久层方面,主流的选择有那些?他们又有什么弊端呢?

1、Spring JDBC

可谓是非常简单的一层JDBC封装了,简洁又方便,缺点在于手动写sql,带来的效率低下和不便于管理。

2、JFianl Model

和JFinal的整个理论非常契合,简洁到极致,问题是不太容易单独抽取出来使用,而且也需要大量手写SQL

3、Hibernate

一直很优秀,从出生就金光闪闪,独特的对象-关系数据库映射让人眼前一亮,比较明显的缺点是联合查询的弱势。

4、Spring JPA

强大的函数名语法解析用起来让人欲罢不能,将提高效率进行到底,缺点和Hibernate类似。

5、Mybatis

解脱了Hibernate的繁重,ORM层变得很清新,缺点在于如果不愿舍弃JPA的巨大优势肿么办呢?

6、speedment

随着微服务的CQRS和函数式编程的兴起,speedment的春风也吹起来了,可以看成是Hibernate进阶版本,缺点是还是需要手动写查询逻辑。

7、思路和借鉴

上述所有的框架中,最让我舒服的就是Spring JPA了,只写方法名就完成了程序的逻辑编写,这样的特性实在太抓心。
不过JPA的联合查询实在让人不能忍啊,而且用select *这种方式效率确实不高。

为了解决这个痛点,我决定基于Mybatis和Spring Data Jpa的思路,用Spring-JDBC为基础自己进行二次开发!

二、示例

先来看一下成果,示例项目在easy-jdbc-sample中,使用了Spring Boot+H2来启动,先看看测试代码:

@SpringBootTest(classes = JujubeJdbcApp.class)
@RunWith(SpringRunner.class)
@ActiveProfiles({"test"})
public class UserDaoTest {
    @Autowired
    private UserDao userDao;

    @Test
    public void findNameById() {
        String name = userDao.findNameById(1);
        assertThat(name).isEqualTo("百度");
    }

    @Test
    public void findNameByAge() {
        List<String> names = userDao.findNameByAge(5);
        assertThat(names).hasSize(2).contains("新浪", "人人网");
    }

    @Test
    public void findByName() {
        User user = userDao.findByName("宇宙女人");
        assertThat(user.getId()).isEqualTo(4);
    }

    @Test
    public void findByNameLike() {
        List<User> users = userDao.findByNameLike("%人%");
        assertThat(users).hasSize(3);
        List<String> names = Collections3.extractToListString(users, "name");
        assertThat(names).contains("女人宇宙", "宇宙女人", "人人网");
    }

    @Test
    public void findByIdGtSortByAgeDesc() {
        List<User> users = userDao.findByIdGtSortByAgeDesc(2);
        assertThat(users).hasSize(9);
        assertThat(users.get(0).getName()).isEqualTo("长白山");
    }

    @Test
    public void getCountByNameLike() {
        int count = userDao.getCountByNameLike("%人%");
        assertThat(count).isEqualTo(3);
    }

    @Test
    public void pageForUserList() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "人");
        map.put("age", 1);
        map.put("ids", Lists.newArrayList(1, 2, 3, 4, 5, 6));
        PageRequest request = new PageRequest(1, 10);
        Page<Record> page = userDao.pageForUserList(map, request);
        assertThat(page.getTotalElements()).isEqualTo(2);
        assertThat(page.getData().get(0).getId()).isEqualTo(3L);
        assertThat(page.getData().get(1).getId()).isEqualTo(4L);
    }

    @Test
    public void pageForUserListOfOrder() {
        PageRequest request = new PageRequest(1, 10);
        Page<Record> page = userDao.pageForUserListOfOrder(new HashMap<>(), request);
        assertThat(page.getTotalElements()).isEqualTo(11L);
        assertThat(page.getData().get(0).getId()).isEqualTo(11L);
    }

}

再看我们的UserDao,非常简洁:

public interface UserDao extends BaseDao<User, Long> {

    @Override
    default String getTableName() {
        return "user";
    }

    public String findNameById(long id);

    public List<String> findNameByAge(int age);

    public User findByName(String name);

    public List<User> findByNameLike(String name);

    public List<User> findByIdGtSortByAgeDesc(int i);

    public int getCountByNameLike(String name);

    public Page<Record> pageForUserList(Map<String, Object> queryMap, PageRequest request);

    public Page<Record> pageForUserListOfOrder(Map<String, Object> queryMap, PageRequest request);

}

Dao中的find和getCount系列方法继承了Spring JPA的神奇之处:根据方法名动态的生成查询语句。至于分页的话,则需要手动写Sql了,后面会讲到如何配置。

UserDao的方法没什么注释,其实是约定大于配置,当你了解了方法名构建的规则,就会知道这些原子操作是什么含义了。

三、Spring Jpa理念与扩展

先来看一下Spring JPA的理念:在查询时,通常需要同时根据多个属性进行查询,且查询的条件也各式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:

  • And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);

  • Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);

  • Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);

  • LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);

  • GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);

  • IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();

  • IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();

  • NotNull --- 与 IsNotNull 等价;

  • Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);

  • NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);

  • OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);

  • Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);

  • In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

  • NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

除了上述规则,框架还可以实现排序,用到SortBy关键字,如:

    public List<User> findByIdGtSortByAgeDesc(int id);

这里是根据年龄进行了倒序查询,Desc后缀表示倒序,Asc表示正序(也是默认值)。

上节的代码中还出现了getCountBy系列方法,规则和Spring JPA一致,是用来查询总数的。

1、智能判断返回类型

对于List<User> findByNameLike(String name)来说,将会自动去查询集合;对于User findByNameLike(String name)来说,将会自动取得top元素。

四、分页

上面说到分页需要些Sql,这个Sql定义在哪儿呢?

定义的UserDao.sql如下:

##pageForUserList
select u.* from `user` u left join  `department` d on u.department_id=d.id
where 1=1
@if name.notBlank
  and u.name like '%${name}%'
@if age > 0
  and u.age > ${age}
@if ids.notNull
  and u.id in (ids.iter(','))
@if nameDesc.notBlank
  order by u.id asc


##pageForUserListOfOrder
select u.* from `user` u left join  `department` d on u.department_id=d.id
order by u.id desc

他的规则非常简单,以##开头后跟Dao中的方法名,对应的就是Dao中同名的方法查询Sql。

1、判断式

看到@if name.notBlank这样的写法,其实是非常符合Java的语法习惯的。@if就是if判断,name.notBlank就是变量name不为null且不为空。

框架内置的有4个boolean判断函数,分别为:notBlank,blank,notNull,null

2、取值

从分页方法入参的map,要从中取值,使用$符号,如${age}

3、iter函数

上面有看到and u.id in (ids.iter(','))的语句,其中ids.iter(',')其实用到了内置函数iter

iter就是循环,ids.iter(',')的意思是:以逗号为分隔符,循环输出ids中的元素。比如ids=[1,2,3],那么in (ids.iter(','))的结果就是in (1,2,3)

五、代码生成工具

在entity-generator项目中打开EntityGeneratorDemo:

    public static void main(String[] args) {
        Config config = new Config("user", "D:\\workspace\\easy-jdbc\\entity-generator\\src\\main\\java", "org.demo.entity", "org.demo.persistence");
        config.setForceCoverDao(false);
        config.setForceCoverEntity(true);
        config.setCreateDao(true);
        EntityGenerator.generateEntity(config);
    }

条件:

  • classpath下需要有application.properties文件,必须的字段如下:

spring.datasource.url=jdbc:mysql://192.168.99.100:3306/demo?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=Aa123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

  • Config配置中需要输入数据库表名、要生成到的项目代码根目录、entity所在包、dao所在包等信息

运行这个类即可在相应的路径中生成Entity和Dao类了。

六、使用

如果你用的是spring-boot的话,maven中加上依赖:

        <dependency>
            <groupId>io.github.jujube-framework</groupId>
            <artifactId>spring-boot-starter-jujube-jdbc</artifactId>
            <version>1.4</version>
        </dependency>

在spring的配置文件中有两个配置项:

jujube.jdbc.base-package=org.jujubeframework.jdbc.persistence
jujube.jdbc.sql-base-package=dao-sql

basePackage是要扫描的Dao所在的包,sqlBasePackage是sql所在的包。注意sql的名称与Dao的名称要一致。

如果是非spring-boot环境,步骤为:

  • 在需要的项目中引入依赖:

        <dependency>
            <groupId>org.jujubeframework</groupId>
            <artifactId>jujube-jdbc</artifactId>
            <version>1.4</version>
        </dependency>

  • 因为这个框架是基于Spring JDBC的,所以你需要先配置一下DataSource和JdbcTemplate。之后加上如下配置:

    @Bean
    public JujubeJdbcConfiguration jujubeJdbcFactoryBean(){
        JujubeJdbcConfiguration jujubeJdbcFactoryBean = new JujubeJdbcConfiguration();
        jujubeJdbcFactoryBean.setBasePackage("org.jujubeframework.jdbc.persistence");
        jujubeJdbcFactoryBean.setSqlBasePackage("dao-sql");
        return jujubeJdbcFactoryBean;
    }

如果用的是xml配置,则如:

<bean name="jujubeJdbcFactoryBean" class="org.jujubeframework.jdbc.spring.JujubeJdbcConfiguration">
    <property name="basePackage" ref="org.jujubeframework.jdbc.persistence"/>
    <property name="sqlBasePackage" ref="dao-sql"/>
</bean>

basePackage是要扫描的Dao所在的包,sqlBasePackage是sql所在的包。注意sql的名称与Dao的名称要一致。

关于sqlBasePackage的路径一般都放在resources下,赋值的时候按照package的形式进行赋值。

如果你想把sql放到src/main/java的classpath下,就必须在Maven的pom.xml中配置:

    <build>
        <resources>
            <resource>
                <filtering>false</filtering>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*Dao.sql</include>
                </includes>
            </resource>
            <resource>
                <filtering>false</filtering>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    </build>

  • 打开entity-generator项目的EntityGeneratorDemo来生成需要的entity和dao

  • package TEST_BYME.day01.tets01; import TEST_BYME.day01.entity.StoreDetails; import org.junit.jupiter.api.Test; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * @Descript

  • http://www.cnblogs.com/bluedream2009/archive/2009/10/23/1588401.html http://www.cnblogs.com/bluedream2009/archive/2009/10/23/1588782.html

  • JDBC之Insert方法 ———————————————— 版权声明:本文为CSDN博主「lykion_881210」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/lykio_881210/article/details/78919961

  • 什么是 JDBC JDBC(Java Database Connectivity)是Java语言中的一种API(应用程序编程接口),用于连接和操作关系型数据库。它提供了一种标准化的方式来访问各种不同的关系型数据库,如MySQL、Oracle、SQL Server等,使得开发人员能够使用Java程序访问和处理这些数据库中的数据。 JDBC定义了一组标准的Java接口和类,它们提供了一种通用的访问数据

  • JDBC(Java Data Base Connectivity,java数据库连接),是一套标准。举个例子,数据库厂家按照这个标志开发驱动,厂商写了一个函数getConnection来链接数据库。以后开发人员就可以通过该函数链接数据库了。这就是标准。所以使用JDBC方式操作数据的话都必须让厂家提供数据库的java 驱动(.jar文件) ODBC(Open Database Connectivit

  • JDBC概述 java提供的一套操作数据库数据的应用编程接口,在用连接jdk所提供的相应实现类。 JDBC组件 DriverManager: 一系列的数据库驱动程序,匹配连接使用通信协议从java应用程序中获取合适的数据库驱动程序。 Connection: 所有和数据库相关的上下文操作通过该接口的实现类来提供数据库的事务操作,Statement对象等接口方法。 Statement:使用创建该接口的

  • 今日内容 1. JDBC基本概念 2. 快速入门 3. 对JDBC中各个接口和类详解 JDBC: 1. 概念:Java DataBase Connectivity Java 数据库连接, Java语言操作数据库 * JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真

  • 首先在项目引入JAR:sqlite-jdbc-(VERSION).jar 连接到数据库(Connecting To Database) 创建一个表(Create a Table) 插入操作(INSERT Operation) 查询操作(SELECT Operation) 更新操作(UPDATE Operation) 删除操作(DELETE Operation)   连接到数据库(Connectin

  • Spring JDBC :Spring框架对JDBC简单的封装。提供了一个JDBCTemplate对象简化了JDBC的开发。 步骤: 1.导入jar包(这些jar包依赖于连接数据库连接池的jar包) 2.创建JDBCTemplate对象,依赖于数据源DataSource JdbcTemplate template = new JdbcTemplate(ds); 3.调用JDBCTemplate

  • Establishing a Connection A JDBC application connects to a target data source using: DriverManager DataSource Using the DriverManager Class Specifying Database Connection URLs MySQL Connector/J Databa

  • 高级JDBC   基本的JDBC使用起来相对简单,但它提供了一组相当有限的与数据库引擎交互的方法。本节将考虑JDBC的一些附加特性,这些特性使客户端能够更多地控制访问数据库的方式。 隐藏驱动程序(Driver)   在基础JDBC中,客户端通过获取Driver对象的实例并调用其连接(connect)方法来连接到数据库引擎。该策略的一个问题是,它将特定于供应商的代码放置到客户端程序中。JDBC包含两

  • JDBC的由来 早期SUN公司想编写一套可以连接所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而

  • # 今日内容     1. 数据库连接池     2. Spring JDBC : JDBC Template ## 数据库连接池     1. 概念:其实就是一个容器(集合),存放数据库连接的容器。             当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。     2. 好处:      

 相关资料
  • 我们在WebLogic11g下使用JSF2.1+PrimeFaces6.0+PrimeFaces-Extensions6.0.0、mojarra 2.1.7。 单击p:commandButton后,DOM inspector显示对话框已在正文和html标记之外创建,如下一个图像所示: 在正文外部呈现的对话框 如果我们使用相同的代码(没有帧)创建一个新的.xhtml,并单击p:commandButt

  • 许多的 PHP 开发者都使用框架,而不是重新造轮子来构建 Web 应用。框架抽象了许多底层常用的逻辑,并提供了有益又简便的方法來完成常见的任务。 你并不一定要在每个项目中都使用框架。有时候原生的 PHP 才是正确的选择,但如果你需要一个框架,那么有如下三种主要类型: 微型框架 全栈框架 组件框架 微型框架基本上是一个封装的路由,用来转发 HTTP 请求至一个闭包,控制器,或方法等等,尽可能地加快开

  • 主要内容:iframe - 设置高度与宽度,实例,iframe - 移除边框,实例,使用 iframe 来显示目标链接页面,实例,HTML iframe 标签通过使用框架,你可以在同一个浏览器窗口中显示不止一个页面。 iframe语法: 该URL指向不同的网页。 iframe - 设置高度与宽度 height 和 width 属性用来定义iframe标签的高度与宽度。 属性默认以像素为单位, 但是你可以指定其按比例显示 (如:"80%")。 实例 <iframe loading="lazy" s

  • tornado.web — RequestHandler and Application classes Thread-safety notes Request handlers Entry points Input Output Cookies Other Application configuration Decorators Everything else tornado.template

  • UI /主题框架和组件 LESS支持下表中列出的一些UI/Theme框架 - Sr.No. 框架和描述 1 1pxdeep 它是平坦的Bootstrap 3主题,提供强大的配色方案控件。 2 Bootflat 它是一个基于Bootstrap的开源框架。 3 BootPress 它是一个基于平面文件CMS的PHP框架 4 Bootstrap 它是功能强大的移动第一前端框架,可实现更快,更轻松的Web

  • 本章介绍 Decoder(解码器) Encoder(编码器) Codec(编解码器) 在前面的章节中,我们讨论了连接到拦截操作或数据处理链的不同方式,展示了如何使用 ChannelHandler 及其相关的类来实现几乎任何一种应用程序所需的逻辑。但正如标准架构模式通常有专门的框架,通用处理模式很适合使用目标实现,可以节省我们大量的开发时间和精力。 在这一章,我们将研究编码和解码——数据从一种特定协