-
Notifications
You must be signed in to change notification settings - Fork 74
- O/R mapping不用设置xml,零配置便于维护
- 不需要了解JDBC的知识
- SQL语句和java代码的分离
- 可以自动生成SQL语句
- 接口和实现分离,不用写持久层代码,用户只需写接口,以及某些接口方法对应的sql 它会通过动态代理自动生成实现类
- 支持自动事务处理和手动事务处理
- Dao整合了Hibernate+mybatis的两大优势,支持实体维护和SQL分离
- SQL支持脚本语言
- 可以无缝集成Hibernate、Spring等第三方框架,也可以单独部署运行,适应性强。
- 支持JPA的CriteriaQuery API,并且提供了类似于Mybatis plus中 QueryWapper和LambdaQueryWapper的这种易用的写法。
1、 项目的pom.xml中引入maven配置
<dependency>
<groupId>com.hbasesoft.framework</groupId>
<artifactId>framework-db-sh</artifactId>
<version>${framework.version}</version>
</dependency>
2、在application.yml中配置数据源
master: #主数据库配置
db:
type: mysql # 数据库类型,支持oracle、h2、mysql 等等, 底层用Hibernate,其支持的改框架都支持
url: jdbc:mysql://192.168.1.1:3306/member?useUnicode=true&characterEncoding=UTF-8&generateSimpleParameterMetadata=true&serverTimezone=Asia/Shanghai
username: test # 用户名
password: sgp # 密码,支持加密模式,密文需要用ENC(密文)包裹
3、具体使用参考下面的章节
db框架底层使用的Hibernate实现的,所以PO的规则和Hibernate保持一致, 可以使用第三方工具生成,我们也提供一个工具直接可以根据数据库中的表生成PO和DAO(Data Access Object)。
- 直接运行com.hbasesoft.framework.db.cg.DBTable2JavaBean类中的main方法,会打开一个UI界面。
- 填写需要生成转化的表名称(不填会查询数据库中所有的表)
- 配置数据库的连接地址和用户名密码
- 点击生成
- 该工具最后还提供了密码加密工具,可以用于application.yml中数据源的密码加密
生成好的DAO会继承 com.hbasesoft.framework.db.core.BaseDao ,里面提供了大量的单表操作方法,当然如果不想使用这些方法,也可以不继承这个接口。
BaseDao.java
public interface BaseDao<T extends BaseEntity> {
CriteriaBuilder criteriaBuilder(); // JPA工厂方法,做一些JPA支持的高级用法
T get(Serializable id); // 根据id来获取数据
<M> M getByCriteria(CriteriaQuery<M> criteria); // 根据条件查询,其中 criteria需要通过CriteriaBuilder来生成,具体参考JPA CriteriaQuery的用法
T getBySpecification(CriterialQuerySpecification<T> specification); // 根据条件查询, PA CriteriaQuery简化写法
T get(QuerySpecification<T> specification); // 根据条件查询, 参考Mybatis plus中 QueryWapper的用法
T getByLambda(LambdaQuerySpecification<T> specification); // 根据条件查询,参考Mybatis plus中 LambdaQueryWapper的用法
List<T> queryAll(); // 查询所有数据
<M> List<M> queryByCriteria(CriteriaQuery<M> criteria); //根据条件查询
List<T> queryBySpecification(CriterialQuerySpecification<T> specification); // 根据条件查询
List<T> query(QuerySpecification<T> specification); // 根据条件查询
List<T> queryByLambda(LambdaQuerySpecification<T> specification); // 根据条件查询
<M> PagerList<M> queryPagerByCriteria(CriteriaQuery<M> criteria, int pageIndex, int pageSize); // 根据条件分页查询
PagerList<T> queryPagerBySpecification(CriterialQuerySpecification<T> specification, int pageIndex, int pageSize); // 根据条件分页查询
PagerList<T> queryPager(QuerySpecification<T> specification, int pageIndex, int pageSize); // 根据条件分页查询
PagerList<T> queryPagerByLambda(LambdaQuerySpecification<T> specification, int pageIndex, int pageSize); // 根据条件分页查询
void save(T entity); // 保存数据
void saveBatch(List<T> entitys); // 批量保存
void update(T pojo); // 更新数据
void updateBatch(List<T> entitys); // 批量更新
void updateByCriteria(CriteriaUpdate<T> criteria); // 根据条件来做更新
void updateBySpecification(CriterialUpdateSpecification<T> specification); // 根据条件来做更新
void update(UpdateSpecification<T> specification); // 根据条件来做更新
void updateByLambda(LambdaUpdateSpecification<T> specification); // 根据条件来做更新
void delete(T entity); // 删除数据
void deleteById(Serializable id); // 根据id来删除
void deleteBatch(Collection<T> entities); //批量删除
void deleteByIds(Collection<? extends Serializable> ids); // 根据id批量删除
void deleteByCriteria(CriteriaDelete<T> criteria); // 根据条件删除
void deleteBySpecification(CriterialDeleteSpecification<T> specification); // 根据条件删除
void delete(DeleteSpecification<T> specification); // 根据条件删除
void deleteByLambda(LambdaDeleteSpecification<T> specification); // 根据条件删除
}
ICourseDao.java
@Dao
public interface ICourseDao extends BaseDao<CourseEntity> {
}
BaseDaoTester.java
/** 依赖spring 注入DAO */
@Resource
private ICourseDao iCourseDao;
/** 根据某个属性查询 */
@Test
public void getByProperty() {
CourseEntity entity = iCourseDao.get(q -> q.eq(CourseEntity.COURSE_NAME, "语文").build());
Assert.equals(entity.getId(), "1", ErrorCodeDef.SYSTEM_ERROR);
}
/** 统计*/
@Test
public void countCourse() {
CriteriaBuilder cb = iCourseDao.criteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<CourseEntity> root = query.from(CourseEntity.class);
query.select(cb.count(root));
Long count = iCourseDao.getByCriteria(query);
Assert.isTrue(count.intValue() == NUM_3, ErrorCodeDef.SYSTEM_ERROR);
}
多表之间的关联查询建议直接写SQL,SQL语句我们支持直接写在@Sql方法注解上, 也支持写成单独文件,文件名称为类名称_方法名称.sql、类名称_方法名称.数据库类型.sql 、数据库类型/类名称_方法名称.sql这三种格式。
IStudentDao.java
@Dao
public interface IStudentDao extends IBaseDao<StudentEntity> {
void createTable();
@Sql("select count(1) from t_student_course sc, t_course c "
+ "where sc.course_id = c.id and sc.score >= 60 and c.course_name = :courseName")
int countCoursePass(@Param("courseName") String courseName);
@Sql("select count(1) from t_student")
int countStudentSize();
/**
* 分页只要在方法上加上@Param(Param.PAGE_INDEX) int pageIndex, @Param(Param.PAGE_SIZE) int pageSize
* 两个参数就会自动分页,SQL中不用写任何关于分页的处理
* 返回的List是com.hbasesoft.framework.db.core.utils.PagerList,这个list可以当成普通list使用,它的信息里面包含了分页所需要的参数<E>
* /
@Sql
PagerList<StudentEntity> queryStudentCourse(@Param("entity") StudentEntity studentEntity,
@Param(Param.PAGE_INDEX) int pageIndex, @Param(Param.PAGE_SIZE) int pageSize);
}
多条SQL用;做分割, 注意结尾不能包含;
IStudentDao_createTable.sql
DROP TABLE IF EXISTS `t_student_course`;
DROP TABLE IF EXISTS `t_student`;
DROP TABLE IF EXISTS `t_course`;
CREATE TABLE `t_student` (
id varchar(32) primary key,
name varchar(32) not null,
age int(3) not null
);
insert into t_student(id, name, age) values ('1', '张三', 18);
insert into t_student(id, name, age) values ('2', '李四', 19);
insert into t_student(id, name, age) values ('3', '王五', 18);
insert into t_student(id, name, age) values ('4', '赵六', 17)
sql内容使用了Velocity模版,支持使用#if #else等语法。注意:为了防止SQL注入,经过模版解析后的sql中的参数需要使用冒号来赋值。
IStudentDao_queryStudentCourse.h2.sql
select
s.id, s.name, s.age, sc.score, c.course_name
from
t_student s,
t_course c,
t_student_course sc
where
s.id = sc.student_id
and
c.id = sc.course_id
#if($entity.name)
and s.name like :entity.name
#end
#if($entity.age)
and s.age = :entity.age
#end
超过一千条以上的数据需要使用 com.hbasesoft.framework.db.hibernate.IBaseDao.executeBatch(String sql, Collection<Object[]> objcts, int commitNumber) 方法来处理。
@Test
public void executeBatch() {
int s1 = iStudentDao.countStudentSize();
// 读取200万行的csv文件, 每次读取10000行
IOUtil.batchProcessFile(new File("Student.csv"), line -> {
if (StringUtils.isNotEmpty(line)) {
String[] strs = StringUtils.split(line, GlobalConstants.SPLITOR);
if (strs.length >= 2) {
return new Object[] {
CommonUtil.getTransactionID(), strs[0], strs[1]
};
}
}
return null;
}, (students, pageIndex, pageSize) -> {
// 批量插入数据,每1000条提交一次
iStudentDao.executeBatch("insert into t_student (id, name, age) values (?, ?, ?)", students,
GlobalConstants.DEFAULT_LINES);
return true;
});
int s2 = iStudentDao.countStudentSize();
Assert.isTrue(s2 - s1 == NUM_200000, ErrorCodeDef.SYSTEM_ERROR_10001);
}
框架支持手动和Spring注解两种方式来控制事务 1、 Spring 注解方式
/*
* 该注解可以打在方法上,也可以打在类上
*/
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
public void transfer(final Integer from, final Integer to, final Float money) {
accountDao.subMoney(from,money);
int i = 1/0;
accountDao.addMoney(to,money);
}
2、 手动控制
// 初始化事务定义
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 设置事务传播方式
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 从事物管理器中获取TransactionManager, 并获取TransactionStatus
TransactionStatus status = TransactionManagerHolder.getTransactionManager().getTransaction(def)
try {
// TODO: 执行业务代码
// 提交当前事务
TransactionManagerHolder.getTransactionManager().commit(status);
} catch(Exception e) {
TransactionManagerHolder.getTransactionManager().rollback(status);
}
框架中支持多数据源配置, 默认系统会加载master数据源,可以通过@DataSource方法注解 或者 DynamicDataSourceManager.setDataSourceCode(String) 来进行多数据源的切换, 这种方式也可以在数据库读写分离的场景使用。
application.yml
master: #主数据库配置
db:
type: mysql
url: jdbc:mysql://192.168.0.1:3306/sgp_agent?useUnicode=true&characterEncoding=UTF-8&generateSimpleParameterMetadata=true&serverTimezone=Asia/Shanghai
username: test
password: ENC(9cOZdwpiByhh7bQcgNDXjNAfUKmujhB2)
gongcheng: #工程数据库配置
db:
type: oracle
driverClass: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@192.168.0.1:1521:CSORCL
username: test
password: ENC(9cOZdwpiByhh7bQcgNDXjNAfUKmujhB2)
validationQuery: SELECT 1 FROM DUAL
hibernate:
dialect: org.hibernate.dialect.OracleDialect
hbm2ddl:
auto: none
show_sql: false
format_sql: false
temp:
use_jdbc_metadata_defaults: false
MaterialServiceImpl.java
@Override
@Transactional
@DataSource(DataSourcePrefixDef.GONG_CHENG)
public DataMaterialEntity getDataMaterialInfoFromGC(String stdCode, String materialCode) {
return materialDao.getDataMaterialInfoFromGC(stdCode, materialCode);
}
不加注解默认为master数据源
DataOrderRecordService.java
@Transactional
void saveDataOrderRecord(DataOrderRecordEntity orderRecordEntity, String stdCode);
OrderController.java
@Override
public String creatGCBill(@RequestBody Tcis2CrmOrderItemInput tcis2CrmOrderItemInput) {
......
try {
List<BillDetail> billDetails = Lists.newArrayList();
if (CollectionUtils.isNotEmpty(tcis2CrmOrderItemInput.getCrmOrderGoods())) {
tcis2CrmOrderItemInput.getCrmOrderGoods().stream()
.filter(crmOrderGoods -> TcisCommonConstant.MATERIAL.equals(crmOrderGoods.getGoodsType()))
.forEach(crmOrderGoods -> {
DataMaterialEntity dataMaterialInfo = materialService.getDataMaterialInfoFromGC(stdCode, crmOrderGoods.getProductSn());
......
});
}
......
} catch (Exception e) {
DataOrderRecordEntity dataOrderRecordEntity = new DataOrderRecordEntity();
.......
dataOrderRecordService.saveDataOrderRecord(dataOrderRecordEntity, stdCode);
}
return gcBill.getExceedID();
}
Framework属于企业级底层开发框架,集成了log、cache、db、message、rule、tx,每块都以模块形式组织,可以根据项目需要获取模块。我们的初衷是屏蔽项目中各种第三方库之间的版本冲突,打造一套屏蔽底层中间件的全新API,提高项目代码的适配能力。
- framework-common 定义公用的常量、工具类 采用了spring-boot方式启动, 启动类为Application, 也可以支持web方式启动。
- framework-log 分布式集成日志模块,详细的记录了每个方法执行的参数、返回结果、执行时间,可以很方便的排查问题或告警,通过远程接口上传服务器(支持直连服务端,也支持通过kafka发送)
- framework-cache 定义了缓存的获取。 支持注解方式访问缓存, 支持基于Redis的分布式锁
- framework-db 是简单易用的轻量级DAO(Data Access Object)框架,它集成了Hibernate实体维护和Mybaits SQL分离的两大优势,提供了非入侵式API,可以与Hibernate、SpringJdbc等数据库框架很好的集成
- framework-job 定时任务,支持quartz、xxl-job、ElasticJob简单封装的定时器,支持分布式、分片等功能
- framework-message 消息模块,通过简单的api发布和订阅事件, 目前支持kafka、redis、rocketMq
- framework-rule 规则引擎,基于json的轻量级规则引擎, 支持多种插件及扩展, 例如:基于状态机的工作流引擎
- framework-tx 分布式事务,支持各种远程接口、同步异步消息。
- [framework-dependencies] 项目依赖,解决版本包依赖问题
- [framework-shell] 控制台方式提供命令操作,支持自定义各种命令,做各种小工具使用。
- [framework-langchain4j] 对langchain4j的补充,支持国内的大模型,让大家更高效的开发AIGC应用。
jdk1.8请使用framework3.X版本,framework4.X已升级至jdk21版本
Hibernate我用了2年半, 13年下半年去中兴软创用了一年SQL服务(软创内部框架), 14年在京东驻场用了2个月的MyBatis,综合了一下这些项目,各有各的优缺点。例如针对复杂业务SQL,hibernate明显能力不足,简单的功能MyBaties也要弄死人,所以一直在思考一个问题有没有一个框架能扬长避短,把大家的优点都发挥出来。 当时在某网站上看了一个帖子介绍了minidao,思路很新颖,拜读了源码。 从此框架之路走起。( 为什么不直接使用minidao,一是这个项目不火、更新节奏也不快,使用风险较大, 二是软创内部使用的都是自己的框架,连spring都没有,hibernate更不可能,jdk都处于1.4、1.5版本,不可能直接使用minidao )
- 14年7月份左右在软创内部gitlab上发布了第一个版本easydao,主要是结合软创当时的系统框架在其之上封装了一层。
- 14年10月份在github上发布了easydao 剥离掉软创内部框架依赖,使其可以不依赖软创的框架,可以结合spring和hibernate,或者可以单独使用jdbc来使用。
- 15年6月开始framework-0.1版本的设计,数据库已经用的很爽了,但是一个项目不仅仅是数据库,还有很多其他东西, 当时针对的是web项目规划了很多模块,类似于现在的web结构, 做了job可以在线管理,消息、rpc、缓存等等功能
- 16年1月22日正式发布1.0版本
- 16年7月21日发布2.0版本,web模块和jeecg合并单独组成framework-manager, framework专门解决项目底层问题
- 17年9月24日发布3.0版本,升级了spring boot版本至2.0, 去掉了dubbox这个rpc框架,引入spring-cloud框架。前端也放弃了jeecg,基于ant-design-pro 实现的一套web框架(目前还未从项目中分离出来,暂未开源)
- 20年2月4日发布了3.4版本, 增加了framework-tx模块,正式支持分布式事务。
- 23年人工智能比较火,又增加了framework-langchain4j,专门扩展国内的一些大模型。
- 中兴视通网上营业厅项目V1.0
- 咪咕在线客服V1.0
- 中国实践教育平台V2.0
- 大丰科创园微信项目V1.0
- 苏州市总工会微信V1.0
- 佛山港华网上营业厅项目V1.0
- 苏州港华网上营业厅项目V1.0
- 苏州体育局微信活动运营项目V1.0
- 苏州市防汛排涝物资管理系统V1.0
- 港华集团网上营业厅项目
- E网通项目
- 港华紫荆微信项目
- 港华物联网平台