# springboot-orm
**Repository Path**: waltertan/springboot-orm
## Basic Information
- **Project Name**: springboot-orm
- **Description**: 一个基于Spring的自研发ORM框架,采用Freemarker语法解析SQL,提供接口注解、XML配置、数据表配置等3种方式进行ORM映射
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-01-26
- **Last Updated**: 2022-01-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# springboot-orm
一个基于Spring的自研发ORM框架,采用Freemarker语法解析SQL,提供接口注解、XML配置、数据表配置等3种方式进行ORM映射。
## 设计类图

## 开始使用
### 配置
* 在classpath下放入sqlset-config.xml文件,内容包括:
```html
扫描的包1
扫描的包2
扫描的包N
```
* 在框架数据源中,创建数据表配置方式映射的基本表BASE_SQLSET,以MySQL为例,数据字段如下:
```sql
create table `BASE_SQLSET` (
`id` varchar (255) COMMENT 'SqlSet的ID',
`sqlType` varchar (255) COMMENT 'select, update, insert, delete',
`dataSource` varchar (255) COMMENT '数据源的beanName',
`statement` blob COMMENT 'SQL语句'
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
```
* 为便于测试,在业务数据源中定义数据表DEPARTMENT,并添加一些数据:
```sql
create table `department` (
`id` bigint (20),
`code` varchar (255),
`name` varchar (255)
);
insert into `department` (`id`, `code`, `name`) values('1','D0001','开发部');
insert into `department` (`id`, `code`, `name`) values('2','D0002','行政部');
insert into `base_sqlset` (`id`, `sqlType`, `dataSource`, `statement`) values('listNameByCode','select','dataSource','select name from department where code like \'%${code}%\'');
insert into `base_sqlset` (`id`, `sqlType`, `dataSource`, `statement`) values('override','select','dataSource','select * from department where code = :code');
```
### 使用接口注解映射
本框架支持注解声明式接口来进行ORM映射。
* 注解说明:
@SqlSet - 声明一个接口为SqlSet类型的接口
属性 | 描述
------------- | -------------
value | 默认使用的数据源beanName(可选)
dataSourceRef | 同value
@Select - 在SqlSet接口中声明一个方法为查询方法
属性 | 描述
------------- | -------------
value | SQL语句
statement | 同value
multiReturnElementType | 配合返回值为集合的方法,定义集合的元素类型(可选,默认为Map)
dataSourceRef | 使用的数据源beanName(可选),可以覆盖@SqlSet中定义的默认数据源
@Insert - 在SqlSet接口中声明一个方法为插入方法
属性 | 描述
------------- | -------------
value | SQL语句
statement | 同value
keyField | 定义主键自增长时,返回的主键值写入哪个字段中(可选)
dataSourceRef | 使用的数据源beanName(可选),可以覆盖@SqlSet中定义的默认数据源
@Delete - 在SqlSet接口中声明一个方法为删除方法
属性 | 描述
------------- | -------------
value | SQL语句
statement | 同value
dataSourceRef | 使用的数据源beanName(可选),可以覆盖@SqlSet中定义的默认数据源
@Update - 在SqlSet接口中声明一个方法为更新方法
属性 | 描述
------------- | -------------
value | SQL语句
statement | 同value
dataSourceRef | 使用的数据源beanName(可选),可以覆盖@SqlSet中定义的默认数据源
@Param - 配合@Update注解使用,在SqlSet接口的更新方法中,声明一个参数作为更新条件
* 使用范例:
定义SqlSet接口:
```java
@SqlSet("dataSource")
public interface DemoRepository {
@Select("select now()")
Date getCurrentDateTime();
@Select("select name from department where id = :id")
String getDepartmentName(Map param);
@Select("select count(0) from department")
long countAll();
}
@SqlSet
public interface Demo1Repository {
@Select(value = "select * from department where 1=1" +
"<#if name??> and name like '%${name}%'#if>" +
"<#if code??> and code like :code#if>",
dataSourceRef = "dataSource")
List> listMapByObject(Object param);
@Select(statement = "select name from department " +
"where 1=1 " +
"and code in (:codes)",
dataSourceRef = "dataSource", multiReturnElementType = String.class)
List listNameByCodeIn(Map params);
@Select(value = "select * from department where 1=1" +
"<#if name??> and name like '%${name}%'#if>" +
"<#if code??> and code like :code#if>",
dataSourceRef = "dataSource", multiReturnElementType = Demo1Domain.class)
List listObjectByMap(Map params);
@Select(statement = "select * from department" +
"where 1=1 " +
"and code in (:codes)", dataSourceRef = "dataSource")
List> listMapByCodeIn(Map params);
@Select(value = "select * from department where 1=1" +
"<#if id??> and id = :id#if>" +
"<#if code??> and code = '${code}'#if>",
dataSourceRef = "dataSource")
Map getMapByObject(Demo1Domain param);
@Select(value = "select * from department where 1=1" +
"<#if id??> and id = :id#if>" +
"<#if code??> and code = '${code}'#if>",
dataSourceRef = "dataSource")
Demo1Domain getObjectByMap(Map params);
@Insert(value = "insert into department(code, name) values (:code, :name)", keyField = "id", dataSourceRef = "dataSource")
int saveObject(Demo1Domain demo1Domain);
@Insert(value = "insert into department(code, name) values (:code, :name)", keyField = "id", dataSourceRef = "dataSource")
int saveMap(Map map);
@Delete(value = "delete from department where name like '%${name}%'", dataSourceRef = "dataSource")
void deleteByObject(Demo1Domain demo1Domain);
@Delete(value = "delete from department where id > :id", dataSourceRef = "dataSource")
int deleteByMap(Map map);
@Update(value = "update department set code = :code where code = :_code", dataSourceRef = "dataSource")
int updateObjectByObject(Demo1Domain newDomain, @Param Demo1Domain param);
@Update(value = "update department set " +
"name = <#if name == '_NULL'>null<#else>:name#if> " +
"where name <#if _name?? && _name == '_NULL'>is null<#else>=:_name#if>"
, dataSourceRef = "dataSource")
int updateObjectByObjectWithNull(Demo1Domain newDomain, @Param Demo1Domain param);
}
```
使用SqlSet接口:
```java
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoRepositoryTests {
@Autowired
private DemoRepository demoRepository;
@Test
public void testDynamicProxy(){
Assert.assertNotNull(demoRepository);
}
@Test
public void testGetCurrentDateTime(){
log.debug(demoRepository.getCurrentDateTime().toString());
}
@Test
public void testGetDepartmentName(){
Map param = new HashMap<>();
param.put("id", 1L);
log.debug(demoRepository.getDepartmentName(param));
}
@Test
public void testCountAll(){
log.debug(String.valueOf(demoRepository.countAll()));
}
}
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo1RepositoryTests {
@Autowired
private Demo1Repository demo1Repository;
@Test
public void testDynamicProxy(){
Assert.assertNotNull(demo1Repository);
}
@Test
public void testListMapByObject(){
List> results = demo1Repository.listMapByObject(new Demo1Domain(null, "D0001", null));
log.debug("result: {}", results.toString());
}
@Test
public void testListObjectByMap(){
Map params = new HashMap<>();
params.put("code", "%D000%");
List> results = demo1Repository.listObjectByMap(params);
log.debug("result: {}", results.toString());
}
@Test
public void testGetObjectByMap(){
Map params = new HashMap<>();
params.put("code", "D0001");
Demo1Domain result = demo1Repository.getObjectByMap(params);
log.debug("result: {}", result.toString());
}
@Test
public void testGetMayByObject(){
Map result = demo1Repository.getMapByObject(new Demo1Domain(2L, null, null));
log.debug("result: {}", result.toString());
}
@Test
public void testListMapByCodeIn(){
Map params = new HashMap<>();
params.put("codes", Sets.newHashSet("D0001", "D0002"));
List> results = demo1Repository.listMapByCodeIn(params);
log.debug("result: {}", results.toString());
}
@Test
public void testListNameByCodeIn(){
Map params = new HashMap<>();
params.put("codes", Sets.newHashSet("D0001", "D0002"));
List results = demo1Repository.listNameByCodeIn(params);
log.debug("result: {}", results.toString());
}
@Test
public void testSaveObject(){
Demo1Domain domain = new Demo1Domain(null, "D0004","财务部");
long count = demo1Repository.saveObject(domain);
Assert.assertTrue(1 == count);
log.debug("result: {}", domain.toString());
}
@Test
public void testSaveMap(){
Map params = new HashMap<>();
params.put("code", "D0003");
params.put("name", "财务部");
long count = demo1Repository.saveMap(params);
Assert.assertTrue(1 == count);
log.debug("result: {}", params.toString());
}
@Test
public void testDeleteByObject(){
Demo1Domain domain = new Demo1Domain(null, null,"财务部");
demo1Repository.deleteByObject(domain);
}
@Test
public void testDeleteByMap(){
Map params = new HashMap<>();
params.put("id", 2L);
long result = demo1Repository.deleteByMap(params);
log.debug("count: {}", result);
}
@Test
public void testUpdateObjectByObject(){
Demo1Domain entity = new Demo1Domain(null, "D0005", null);
Demo1Domain param = new Demo1Domain(null, "D0004", null);
long result = demo1Repository.updateObjectByObject(entity, param);
log.debug("count: {}", result);
}
@Test
public void testUpdateObjectByObjectWithNull(){
Demo1Domain entity = new Demo1Domain(null, null, "XXX");
Demo1Domain param = new Demo1Domain(null, null, "_NULL");
long result = demo1Repository.updateObjectByObjectWithNull(entity, param);
log.debug("count: {}", result);
}
@Test
public void testUpdateObjectByObjectWithNotNull(){
Demo1Domain entity = new Demo1Domain(null, null, "_NULL");
Demo1Domain param = new Demo1Domain(null, null, "XXX");
long result = demo1Repository.updateObjectByObjectWithNull(entity, param);
log.debug("count: {}", result);
}
}
```
### 使用XML配置映射
除了注解声明式接口方式外,本框架还支持通过编写XML映射文件来进行ORM映射。
XML映射文件必须放在classpath能访问到的路径之下,且文件名必须以-SqlSet.xml结尾,可以有多个不同名的XML映射文件。
映射文件的根元素\中,可以用dataSourceRef指定默认数据源,其子元素\