侧边栏壁纸
博主头像
术业有道之编程博主等级

亦是三月纷飞雨,亦是人间惊鸿客。亦是秋霜去叶多,亦是风华正当时。

  • 累计撰写 99 篇文章
  • 累计创建 50 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

java快速上手kotlin

Administrator
2022-05-13 / 0 评论 / 0 点赞 / 2 阅读 / 8526 字

写在前面

我使用kotlin开发已经6年了,一路被kotlin坑过来的,不过当下我觉得已经相对完美了(没有什么坑了)。
推荐java开发者都可以学习一下kotlin,理由就一个字(学的快,开发也快),同样的技术栈,同样的业务,kotlin开发比java最少快3倍。
我不打算科普javakotlin的区别和特性,有兴趣的就继续看。下面我会从java、springboot等主流技术栈换成kotlin来展示一个业务开发的过程。

一、开发环境

  • ideakotlin 插件
  • open jdk 8
  • mavengradle(推荐用gradle,它会自动配置帮你解决字节码生成时机的问题,在maven下要一通配置)

二、创建项目

idea中创建一个springboot项目,语言选择kotlin即可
创建springboot-kotlin项目

三、创建数据表实体

我这里使用的是hibernate+jpa,如果是用mybatis,去掉相应注解,换成mybatis注解或申明一个cofig clss配置xml即可


import com.fasterxml.jackson.annotation.JsonFormat
import org.hibernate.annotations.DynamicInsert
import org.hibernate.annotations.DynamicUpdate
import java.time.LocalDateTime
import javax.persistence.*
import com.kdj.server.domain.common.dto.ImagesDTO
import com.kdj.server.domain.converter.ListImagesDTOConverter

@Entity
@Table(
    name = "user",
    indexes = [
        Index(name = "deleted", columnList = "deleted")
    ]
)
@DynamicUpdate
@DynamicInsert
data class UserModel(
    @Id
    @Column(name = "id", columnDefinition = "bigint(20) COMMENT 'ID,自增'")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null,

     @Column(name = "images", columnDefinition = "longtext COMMENT '图片'")
    @Convert(converter = ListImagesDTOConverter::class)
    var images: MutableList<ImagesDTO>? = null,

    @Column(name = "deleted", columnDefinition = "bigint(2) DEFAULT 0 COMMENT '是否删除,0未删除 1已删除'", nullable = false)
    var deleted: Boolean? = null,

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Column(name = "create_at", columnDefinition = "datetime COMMENT '创建时间'", nullable = false)
    var createAt: LocalDateTime? = null
) {


    /**
     * 触发jpa update代码需要执行的逻辑
     */
    @PreUpdate
    fun preUpdate() {
        
    }

    /**
     * 自动设置必要字段的值
     */
    @PrePersist
    fun prePersist() {
        createAt= LocalDateTime.now()
        deleted = false
    }
}

配置application.yml内容spring.jpa.hibernate.ddl-auto: update,会自动根据表更改来建表增加字段。
当前方式需要注意以下问题:

  • 索引只会在自动建表时新建,后面的索引更改不会自动应用
  • 当前ddl-auto:update策略会增加表字段,不会删除和更改表字段,可以在生产使用,但是不建议。我们有必要保证线上的数据库表是非常精准的。
  • hibernateupdate操作实际上是查询对比差异后再改动,在Batch update时会有严重的性能问题以及jdbc连接池爆满的情况。
  • javakotlin中都存在pId这种属性声明会变成PID的情况,要避免3个字符的驼峰命名

四、使用spring data jpa

  • 在项目的根目录build.gradle.ktsdependencies下增加:
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
  • 如果要加入querydsl
    • 在项目的根目录build.gradle.ktsplugins下增加:
     kotlin("kapt") version "1.4.20"
    
    • 在项目的根目录build.gradle.ktsdependencies下增加:
    implementation("com.querydsl:querydsl-jpa:5.0.0")
    kapt("com.querydsl:querydsl-apt:5.0.0:jpa")
    
  • 剩下的就是几乎一样了,按之前java使用spring data jpa的方式,新建一个kotlin interface来写Repository即可
    springdatajpa-kotlin-Repository

五、使用自定义的converter

当我们需要用一个字符串存储一个格式化的json、数组或者对象时,可以使用这个自定义的converter
例子:在三、创建数据表实体中有一个images属性

@Convert(converter = ListImagesDTOConverter::class)
var images: MutableList<ImagesDTO>? = null,
  • 先编写baseConverter,来增加扩展能力
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.util.StringUtils
import java.lang.reflect.ParameterizedType
import javax.persistence.AttributeConverter

abstract class BaseConverter<E> : AttributeConverter<List<E>?, String?> {
    private var clazz: Class<E> = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<E>
    val objectMapper = ObjectMapper()
    override fun convertToDatabaseColumn(list: List<E>?): String {
        if (list == null || list.isEmpty()) return ""
        return objectMapper.writeValueAsString(list)
    }

    override fun convertToEntityAttribute(json: String?): MutableList<E> {
        if (!StringUtils.hasText(json)) return mutableListOf()
        return objectMapper.readValue(json, getCollectionType(List::class.java, clazz))
    }

    /**
     * 获取泛型的Collection Type
     *
     * @param collectionClass 泛型的Collection
     * @param elementClasses  元素类
     * @return JavaType Java类型
     * @since 1.0
     */
    private fun getCollectionType(collectionClass: Class<*>, vararg elementClasses: Class<*>): JavaType {
        return objectMapper.typeFactory.constructParametricType(collectionClass, *elementClasses)
    }
}
  • 编写基于ImagesDTO类的List Converter
import com.kdj.server.domain.common.dto.ImagesDTO
import javax.persistence.Converter

@Converter
class ListImagesDTOConverter : BaseConverter<ImagesDTO>()

到这里就完成了,用法在上面已经贴了2次了

六、使用mapstruct

kotlindata数据类已经有lombok的所有功能,并且还有增强,一般情况下不需要使用mapstruct

  • 在项目的根目录build.gradle.ktsplugins下增加(有就不用加了):
kotlin("kapt") version "1.4.20"
  • 在项目的根目录build.gradle.ktsdependencies下增加:
implementation("org.mapstruct:mapstruct:1.5.0.RC1")
kapt("org.mapstruct:mapstruct-processor:1.5.0.RC1")
  • 编写自定义interface Mapstruct
import com.kdj.server.domain.model.UserModel
import com.kdj.server.support.minapp.domain.model.MinUserInfoModel
import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.MappingConstants
import org.mapstruct.Mappings

/**
 * 注意这个类的 pId 要写成 PID 这种规则
 */
@Mapper(
    componentModel = MappingConstants.ComponentModel.SPRING
)
interface UserModelMapstruct {

    @Mappings(
        Mapping(ignore = true, target = "id"),
        Mapping(ignore = true, target = "deleted"),
        Mapping(ignore = true, target = "createAt"),
        Mapping(ignore = true, target = "roles"),
        Mapping(source = "nickName", target = "name"),
        Mapping(source = "headImgUrl", target = "avatar")
    )
    fun toUserModel(minUserInfoModel: MinUserInfoModel): UserModel
}
  • 关于自定义的转换
    场景:有些情况,我们希望在转换的过程中通过调用一些方法来得到一个结果,将这个结果传给目标。
@Mappings(
   Mapping(
            target = "releaseTime",
            expression = "java(new TypeConversionWorker().toStrDate(kHLDetailsVO.getCreateTime(), DateStyle.YYYY_MM_DD))"
   )
)
fun toServiceProvidersModel(kHLDetailsVO: KHLDetailsVO): ServiceProvidersModel

说明:
这个expression

0

评论区