Skip to content

Commit

Permalink
完善 Active Record 相关的 FenixModel 的代码
Browse files Browse the repository at this point in the history
  • Loading branch information
blinkfox committed Mar 28, 2022
1 parent 051ee49 commit 5095ae5
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 26 deletions.
4 changes: 2 additions & 2 deletions blinkfox-checks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@
</module>
<!-- 检查类中的泛型参数名称是否符合 format 中定义的格式. -->
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]*[ID]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
Expand All @@ -271,7 +271,7 @@
</module>
<!-- 检查接口中的的泛型参数名称是否符合 format 中定义的格式. -->
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]*[ID]$)"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
Expand Down
11 changes: 0 additions & 11 deletions checkstyle-suppressions.xml

This file was deleted.

102 changes: 91 additions & 11 deletions src/main/java/com/blinkfox/fenix/ar/FenixModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
import lombok.Setter;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;

/**
* Fenix 提供的 ActiveRecord 模式的基础 Model 类. TODO 待进一步开发...
* Fenix 提供的 ActiveRecord 模式的基础 Model 类.
*
* <p>本类仅提供操作单个实体对象的若干快捷方法,如果你想进行批量操作或者查询,可以通过本类中的 {@link #getRepository()}、
* {@link #getJpaRepository()} 或者 {@link #getFenixJpaRepository()} 等方法来调用或实现.</p>
*
* @param <T> 实体类的的泛型参数
* @param <ID> 主键 ID
Expand All @@ -32,11 +36,14 @@ public abstract class FenixModel<T, ID> {
private CrudRepository<T, ID> repository;

/**
* 获取实体类的 ID 值.
* 获取本实体对象的 ID 值.
*
* <p>通常建议将实体类的 ID 字段取名为 {@code id},这样就能自动重写该字段的值,
* 否则当你继承本抽象类时,就需要你自己手动实现此方法.</p>
*
* @return ID
* @return ID
*/
abstract ID getEntityId();
public abstract ID getId();

/**
* 获取本实体类所对应的 Repository 对象在 Spring 中 Bean 的名称.
Expand All @@ -61,7 +68,7 @@ private String getRepositoryClassName() {
}

/**
* 获取本实体类所对应的 Repository 对象
* 获取本实体类所对应的 Repository 对象.
*
* @return 基本的 CrudRepository 对象
*/
Expand Down Expand Up @@ -104,6 +111,23 @@ private void initCheckAndSetRepository() {
}
}

/**
* 获取本实体类所对应的 {@link JpaRepository} 的对象实例.
*
* <p>注意:要能真正获取到 {@link JpaRepository} 的对象实例,你需要确保你实体类所对应的 Repository 接口
* 是继承了 {@link JpaRepository} 接口的,否则会抛出 {@link FenixException} 异常.</p>
*
* @return 本实体类的 JpaRepository 对象实例
*/
public JpaRepository<T, ID> getJpaRepository() {
CrudRepository<T, ID> crudRepository = this.getRepository();
if (crudRepository instanceof JpaRepository) {
return (JpaRepository<T, ID>) crudRepository;
}
throw new FenixException("【Fenix 异常】未获取到本实体类所对应的 JpaRepository 的对象实例,请确保你实体类所对应的 "
+ "Repository 接口已经继承了 JpaRepository 接口!");
}

/**
* 获取本实体类所对应的 {@link FenixJpaRepository} 的对象实例.
*
Expand All @@ -118,7 +142,7 @@ public FenixJpaRepository<T, ID> getFenixJpaRepository() {
return (FenixJpaRepository<T, ID>) crudRepository;
}
throw new FenixException("【Fenix 异常】未获取到本实体类所对应的 FenixJpaRepository 的对象实例,请确保你实体类所对应的 "
+ "Repository 接口是继承了 FenixJpaRepository 接口的!");
+ "Repository 接口已经继承了 FenixJpaRepository 接口!");
}

/**
Expand All @@ -133,20 +157,68 @@ public T save() {
}

/**
* 保存本实体对象.
* 立即刷新(提交)数据到数据库中.
*/
@Transactional
public void flush() {
this.getJpaRepository().flush();
}

/**
* 保存并刷新(提交)本实体对象的数据到数据库中.
*
* @return 保存后的对象
*/
@Transactional
public <S extends T> Iterable<S> saveAll(Iterable<S> entities) {
return this.getRepository().saveAll(entities);
@SuppressWarnings("unchecked")
public <S extends T> S saveAndFlush() {
return this.getJpaRepository().saveAndFlush((S) this);
}

/**
* 根据本实体对象的 ID 查找数据库中的完整的实体对象记录信息.
* 新增或更新本实体对象中所有非 null 属性的字段值.
*
* <p>注意:该方法保存每条数据时会先查询判断本对象在数据库中是否存在,再根据是否存在的结果再进行插入或者更新.</p>
*
* <ul>
* <li>如果实体对象的主键 ID 为空,说明是新增的情况,就插入一条新的数据;</li>
* <li>如果实体对象的主键 ID 不为空,会先判断是否存在该 ID 的数据,如果不存在也会新增插入一条数据;
* 否则说明是更新的情况,会仅更新实体类属性中不为 null 值的属性字段到数据库中;</li>
* </ul>
*
* @return 原实体类,注意:如果是更新的情况,返回的值不一定有数据库中之前的值
*/
@Transactional
@SuppressWarnings("unchecked")
public <S extends T> S saveOrUpdateByNotNullProperties() {
return this.getFenixJpaRepository().saveOrUpdateByNotNullProperties((S) this);
}

/**
* 根据本实体对象的 ID 查找数据库中的完整的实体对象记录信息,本实体对象信息会以 Optional 来包裹返回.
*
* @return 本实体对象的 Optional 对象
*/
public Optional<T> findById() {
return this.getRepository().findById(this.getEntityId());
return this.getRepository().findById(this.getId());
}

/**
* 根据本实体对象的 ID 查找数据库中的完整的实体对象记录信息.
*
* @return 本实体对象信息
*/
public T getById() {
return this.getJpaRepository().getById(this.getId());
}

/**
* 根据本实体对象的 ID 查找数据库中是否存在该对象.
*
* @return 布尔值
*/
public boolean existsById() {
return this.getRepository().existsById(this.getId());
}

/**
Expand All @@ -158,4 +230,12 @@ public void delete() {
this.getRepository().delete((T) this);
}

/**
* 根据本实体对象的 ID 值查找并删除此对象.
*/
@Transactional
public void deleteById() {
this.getRepository().deleteById(this.getId());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public <S extends T> void updateBatch(Iterable<S> entities, int batchSize) {
* @param <S> 泛型实体类
* @return 原实体类,注意:如果是更新的情况,返回的值不一定有数据库中之前的值
*/
@SuppressWarnings("unchecked")
@Transactional(rollbackFor = RuntimeException.class)
@Override
public <S extends T> S saveOrUpdateByNotNullProperties(S entity) {
Expand Down Expand Up @@ -284,8 +285,7 @@ private String[] getNullProperties(Object entity) {
List<String> nullProperties = new ArrayList<>();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
String propertyName = propertyDescriptor.getName();
Object propertyValue = beanWrapper.getPropertyValue(propertyName);
if (propertyValue == null) {
if (beanWrapper.getPropertyValue(propertyName) == null) {
nullProperties.add(propertyName);
}
}
Expand Down
68 changes: 68 additions & 0 deletions src/test/java/com/blinkfox/fenix/entity/ar/ArEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.blinkfox.fenix.entity.ar;

import com.blinkfox.fenix.ar.FenixModel;
import java.time.LocalDateTime;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.hibernate.annotations.GenericGenerator;

/**
* 用来测试 Active Record 的实体类.
*
* @author blinkfox on 2022-03-28.
* @since 2.7.0
*/
@Getter
@Setter
@Accessors(chain = true)
@Entity
@Table(name = "t_ar_table")
public class ArEntity extends FenixModel<ArEntity, String> {

/**
* ID.
*/
@Id
@Column(name = "c_id")
@GeneratedValue(generator = "nanoId")
@GenericGenerator(name = "nanoId", strategy = "com.blinkfox.fenix.id.NanoIdGenerator")
private String id;

/**
* 标题.
*/
@Column(name = "c_title")
private String title;

/**
* 描述.
*/
@Column(name = "c_desc")
private String desc;

/**
* 年龄.
*/
@Column(name = "n_age")
private Integer age;

/**
* 创建时间.
*/
@Column(name = "dt_create_time")
private Date createTime;

/**
* 最后更新时间.
*/
@Column(name = "dt_update_time")
private LocalDateTime lastUpdateTime;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.blinkfox.fenix.repository.ar;

import com.blinkfox.fenix.entity.ar.ArEntity;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

/**
* ArEntity 所对应的 Repository.
*
* @author blinkfox on 2022-03-28.
* @since 1.0.0
*/
@Repository
public interface ArEntityRepository extends CrudRepository<ArEntity, String> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.blinkfox.fenix.repository.ar;

import com.blinkfox.fenix.FenixTestApplication;
import com.blinkfox.fenix.entity.ar.ArEntity;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Optional;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
* ArEntityRepository 的单元测试类.
*
* @author blinkfox on 2022-03-28.
* @since v2.7.0
*/
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FenixTestApplication.class)
public class ArEntityRepositoryTest extends BaseRepositoryTest {

public static final String TITLE = "这是测试标题";

/**
* 初始化.
*/
@PostConstruct
public void init() {
super.initLoad();
}

@Test
public void saveEntity() {
ArEntity arEntity = new ArEntity()
.setTitle(TITLE)
.setDesc("这是测试的描述信息")
.setAge(20)
.setCreateTime(new Date())
.setLastUpdateTime(LocalDateTime.now())
.save();

// 查询保存的结果.
Optional<ArEntity> arOption = arEntity.findById();
Assert.assertTrue(arOption.isPresent());

// 断言查询结果是否正确.
ArEntity newArEntity = arOption.get();
Assert.assertNotNull(newArEntity.getId());
Assert.assertEquals(newArEntity.getTitle(), TITLE);
Assert.assertNotNull(newArEntity.getDesc());
Assert.assertNotNull(newArEntity.getAge());
Assert.assertNotNull(newArEntity.getCreateTime());
Assert.assertNotNull(newArEntity.getLastUpdateTime());

// 再次查询断言.
Optional<ArEntity> arOption2 = newArEntity.getRepository().findById(newArEntity.getId());
Assert.assertTrue(arOption2.isPresent());
Assert.assertEquals(arOption2.get().getTitle(), newArEntity.getTitle());
}

}
Loading

0 comments on commit 5095ae5

Please sign in to comment.