JPA Tools API - lifedever/kt-speedy-toolbox GitHub Wiki

JPA 工具 API

kt-speedy-data-jpa 模块提供了 Spring Data JPA 的增强工具,包括基础实体类、服务层基类、查询构建器等,简化 JPA 开发。

📋 目录

基础实体类

SupportModal

提供了包含审计字段的基础实体类,自动处理创建时间、修改时间、创建人、修改人等字段。

@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
open class SupportModal : Serializable

字段说明:

  • id: String? - 主键,自动生成19位UUID
  • createdDate: Date? - 创建时间,自动设置
  • lastModifiedDate: Date? - 最后修改时间,自动更新
  • createdBy: String? - 创建人,需要配置审计
  • lastModifiedBy: String? - 最后修改人,需要配置审计
  • deleted: Boolean? - 删除标识,默认false

使用示例:

@Entity
@Table(name = "users")
class User : SupportModal() {
    @Column(name = "username")
    var username: String? = null
    
    @Column(name = "email")
    var email: String? = null
    
    @Column(name = "phone")
    var phone: String? = null
}

基础仓库接口

SupportRepository

扩展了 JpaRepository 和 JpaSpecificationExecutor,提供了额外的查询方法。

interface SupportRepository<T : SupportModal, ID : Serializable> : 
    JpaRepository<T, ID>, JpaSpecificationExecutor<T>

使用示例:

@Repository
interface UserRepository : SupportRepository<User, String> {
    fun findByUsername(username: String): User?
    fun findByEmail(email: String): User?
}

基础服务类

SupportService

提供了通用的服务层基类,包含常用的 CRUD 操作和查询方法。

@Transactional(readOnly = true)
abstract class SupportService<T : SupportModal, ID : Serializable>

核心方法:

查询方法

fun findById(id: ID): Optional<T>
fun findAllById(ids: MutableList<ID>): MutableList<T>
fun getOne(id: ID): T // 找不到时抛出异常
fun getOneOrNull(id: ID?): T? // 找不到时返回null
fun findAll(): List<T>
fun findAll(sort: Sort): List<T>
fun findAll(spec: Specification<T>): List<T>
fun findAll(spec: Specification<T>, pageable: Pageable): Page<T>
fun findAll(pageable: Pageable): Page<T>

保存方法

@Transactional
open fun save(t: T): T
@Transactional
open fun saveAll(entities: List<T>): List<T>

删除方法

@Transactional
open fun deleteById(id: ID)
@Transactional
open fun delete(entity: T)
@Transactional
open fun deleteAll()
@Transactional
open fun deleteAllById(ids: List<ID>)
@Transactional
open fun deleteLogic(id: ID) // 逻辑删除

统计方法

fun count(): Long
fun count(spec: Specification<T>): Long

使用示例:

@Service
class UserService : SupportService<User, String>() {
    
    @Autowired
    override lateinit var repository: UserRepository
    
    fun findByUsername(username: String): User? {
        return repository.findByUsername(username)
    }
    
    fun createUser(username: String, email: String): User {
        val user = User().apply {
            this.username = username
            this.email = email
        }
        return save(user)
    }
    
    fun getUsersWithPagination(page: Int, size: Int): Page<User> {
        val pageable = PageRequest.of(page, size, Sort.by("createdDate").descending())
        return findAll(pageable)
    }
}

查询构建器

SpecificationBuilder

提供了链式构建 JPA Specification 查询的工具。

class SpecificationBuilder<T> private constructor()

核心方法:

companion object {
    fun <T> builder(): SpecificationBuilder<T>
}

fun and(newSpec: Specification<T>): SpecificationBuilder<T>
fun or(newSpec: Specification<T>): SpecificationBuilder<T>
fun build(): Specification<T>

使用示例:

@Service
class UserService : SupportService<User, String>() {
    
    fun searchUsers(username: String?, email: String?, isActive: Boolean?): List<User> {
        val spec = SpecificationBuilder.builder<User>()
            .apply {
                username?.let { 
                    and(Specification.where { root, _, cb ->
                        cb.like(root.get("username"), "%$it%")
                    })
                }
                email?.let {
                    and(Specification.where { root, _, cb ->
                        cb.equal(root.get("email"), it)
                    })
                }
                isActive?.let {
                    and(Specification.where { root, _, cb ->
                        cb.equal(root.get("deleted"), !it)
                    })
                }
            }
            .build()
            
        return findAll(spec)
    }
}

ID 生成器

IdKit

提供了19位UUID生成器,确保ID的唯一性和可读性。

object IdKit {
    fun get(): String // 生成19位UUID
    fun getUUID(): String // 生成32位UUID(无横线)
}

使用示例:

val shortId = IdKit.get()     // "2Hs7K8mN9pQ3rT5vW"
val longId = IdKit.getUUID()  // "550e8400e29b41d4a716446655440000"

事务工具

TransactionUtil

提供了事务同步工具,可以在事务提交后执行特定操作。

object TransactionUtil {
    fun triggerAfterCommit(call: () -> Unit)
}

使用示例:

@Service
class UserService : SupportService<User, String>() {
    
    @Transactional
    fun createUserWithNotification(username: String, email: String): User {
        val user = User().apply {
            this.username = username
            this.email = email
        }
        val savedUser = save(user)
        
        // 事务提交后发送通知
        TransactionUtil.triggerAfterCommit {
            sendWelcomeEmail(savedUser.email!!)
        }
        
        return savedUser
    }
    
    private fun sendWelcomeEmail(email: String) {
        // 发送欢迎邮件的逻辑
        println("发送欢迎邮件到: $email")
    }
}

使用示例

完整的实体和服务示例

// 1. 实体类
@Entity
@Table(name = "articles")
class Article : SupportModal() {
    @Column(name = "title", nullable = false)
    var title: String? = null
    
    @Column(name = "content", columnDefinition = "TEXT")
    var content: String? = null
    
    @Column(name = "author")
    var author: String? = null
    
    @Column(name = "published")
    var published: Boolean = false
}

// 2. 仓库接口
@Repository
interface ArticleRepository : SupportRepository<Article, String> {
    fun findByTitleContaining(title: String): List<Article>
    fun findByAuthorAndPublished(author: String, published: Boolean): List<Article>
}

// 3. 服务类
@Service
class ArticleService : SupportService<Article, String>() {
    
    @Autowired
    override lateinit var repository: ArticleRepository
    
    fun publishArticle(id: String): Article {
        val article = getOne(id)
        article.published = true
        return save(article)
    }
    
    fun searchArticles(keyword: String?, author: String?, published: Boolean?): List<Article> {
        val spec = SpecificationBuilder.builder<Article>()
            .apply {
                keyword?.let {
                    and(Specification.where { root, _, cb ->
                        cb.or(
                            cb.like(root.get("title"), "%$it%"),
                            cb.like(root.get("content"), "%$it%")
                        )
                    })
                }
                author?.let {
                    and(Specification.where { root, _, cb ->
                        cb.equal(root.get("author"), it)
                    })
                }
                published?.let {
                    and(Specification.where { root, _, cb ->
                        cb.equal(root.get("published"), it)
                    })
                }
            }
            .build()
            
        return findAll(spec)
    }
    
    fun getPublishedArticlesPaged(page: Int, size: Int): Page<Article> {
        val spec = Specification.where<Article> { root, _, cb ->
            cb.equal(root.get<Boolean>("published"), true)
        }
        val pageable = PageRequest.of(page, size, Sort.by("createdDate").descending())
        return findAll(spec, pageable)
    }
}

配置审计功能

@Configuration
@EnableJpaAuditing
class JpaConfig {
    
    @Bean
    fun auditorProvider(): AuditorAware<String> {
        return AuditorAware {
            // 返回当前用户ID,可以从Security Context获取
            Optional.of("system") // 这里简化为固定值
        }
    }
}

📝 注意事项

  1. 审计功能: 需要启用 @EnableJpaAuditing 并配置 AuditorAware
  2. 逻辑删除: deleteLogic() 方法只是设置 deleted 字段为 true,不会物理删除数据
  3. 事务管理: 服务类默认开启只读事务,写操作方法需要 @Transactional 注解
  4. ID生成: 实体类会自动生成19位UUID作为主键
  5. 查询构建: 使用 SpecificationBuilder 可以灵活构建复杂查询条件

🔗 相关链接

⚠️ **GitHub.com Fallback** ⚠️