1

I need get data from database without some fields, but I receive or ALL data (with relationship) or error.

import org.springframework.data.domain.Persistable
import org.springframework.data.util.ProxyUtils
import java.io.Serializable
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.MappedSuperclass
import javax.persistence.Transient

@MappedSuperclass
abstract class AbstractEntity<T : Serializable>() : Persistable<T> {

    companion object {
        private val serialVersionUID = -5554308939380869754L
    }

    @Id
    @GeneratedValue
    private var id: T? = null

    override fun getId(): T? {
        return id
    }

    fun setId(id:T){
        this.id = id
    }

    /**
     * Must be [Transient] in order to ensure that no JPA provider complains because of a missing setter.
     *
     * @see org.springframework.data.domain.Persistable.isNew
     */
    @Transient
    override fun isNew() = null == getId()

    override fun toString() = "Entity of type ${this.javaClass.name} with id: $id"

    override fun equals(other: Any?): Boolean {
        other ?: return false

        if (this === other) return true

        if (javaClass != ProxyUtils.getUserClass(other)) return false

        other as AbstractEntity<*>

        return if (null == this.getId()) false else this.getId() == other.getId()
    }

    override fun hashCode(): Int {
        return 31
    }
}

ProductEntity:

@Entity
@Table(name = "product")
class ProductEntity : AbstractEntity<Long> {
    @Column(name = "product_name")
    var productName: String = ""
    @Column(name = "description", length = 65535, columnDefinition = "text")
    var description: String = ""
    @Column(name = "product_model")
    var productModel: String = ""
    @Column(name = "product_articul")
    var productArticul: String = ""
    @Column(name = "product_count")
    var productCount: Int = 0
    @Column(name = "is_available")
    var isAvailable: Boolean = false
    @Column(name = "product_price")
    var productPrice: Float = 0f
    @Column(name = "product_extra")
    var productExtra: Float = 0f
    @Column(name = "product_total_price")
    var productTotalPrice: Float = 0f
    @OneToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE], orphanRemoval = false, fetch = FetchType.LAZY)
    var productCategoryProperty: ProductCategoryPropertyEntity = ProductCategoryPropertyEntity()
    @OneToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE], fetch = FetchType.LAZY)
    var category: CategoryEntity = CategoryEntity()
    @Column(name = "images")
    var images: Array<String?> = arrayOf()
    @Column(name = "keywords", length = 65535, columnDefinition = "text")
    var keywords: String? = ""

    constructor() : super() {
    }

    fun convertToDto(): ProductDto {
        return ProductDto(id, productName, description, productModel, productArticul,
                productCount, productPrice, productExtra, productTotalPrice, isAvailable,
                productCategoryProperty?.convertToDto(), category?.convertToDto(), images, keywords)
    }
}

ProductCategoryPropertyEntity:

import ua.jdroidcoder.jdcshop.dto.ProductCategoryPropertyDto
import javax.persistence.*

@Entity
@Table(name = "product_category_property")
class ProductCategoryPropertyEntity : AbstractEntity<Long> {

    @OneToMany(cascade = [CascadeType.PERSIST, CascadeType.MERGE], orphanRemoval = false)
    var simpleProperties: MutableList<ProductPropertySimpleEntity> = ArrayList()

    @OneToMany(cascade = [CascadeType.PERSIST, CascadeType.MERGE], orphanRemoval = false)
    var advancedProperties: MutableList<ProductPropertySimpleEntity> = ArrayList()

    constructor() : super() {
    }

    fun convertToDto(): ProductCategoryPropertyDto {
        val simple = ProductCategoryPropertyDto()
        simple.id = id
        simpleProperties?.forEach {
            simple.simpleProperties.add(it.convertToDto())
        }
        advancedProperties?.forEach {
            simple.advancedProperties.add(it.convertToDto())
        }
        return simple
    }
}

ProductPropertySimpleEntity:

import ua.jdroidcoder.jdcshop.dto.PropertySimpleDto
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Table

@Entity
@Table(name = "product_property_simple")
class ProductPropertySimpleEntity : AbstractEntity<Long> {
    @Column(name = "property_name")
    var propertyName: String = ""

    @Column(name = "property_value")
    var propertyValue: String = ""

    @Column(name = "parent_id")
    var parentId: Long? = null

    @Column(name = "parent_id_in_category")
    var parentIdInCategory: Long? = null

    @Column(name = "id_in_category")
    var idInCategory: Long? = null

    constructor() : super() {
    }

    fun convertToDto(): PropertySimpleDto {
       return PropertySimpleDto(id!!,propertyName,parentId,propertyValue)
    }
}

When I try make select, I see:

Hibernate: select productent0_.id as id1_3_, productent0_.category_id as categor13_3_, productent0_.description as descript2_3_, productent0_.images as images3_3_, productent0_.is_available as is_avail4_3_, productent0_.keywords as keywords5_3_, productent0_.product_articul as product_6_3_, productent0_.product_category_property_id as product14_3_, productent0_.product_count as product_7_3_, productent0_.product_extra as product_8_3_, productent0_.product_model as product_9_3_, productent0_.product_name as product10_3_, productent0_.product_price as product11_3_, productent0_.product_total_price as product12_3_ from product productent0_
Hibernate: select categoryen0_.id as id1_0_0_, categoryen0_.category_name as category2_0_0_, categoryen0_.prom_category_id as prom_cat3_0_0_ from category categoryen0_ where categoryen0_.id=?
Hibernate: select productcat0_.id as id1_4_0_ from product_category_property productcat0_ where productcat0_.id=?
Hibernate: select simpleprop0_.product_category_property_entity_id as product_1_6_0_, simpleprop0_.simple_properties_id as simple_p2_6_0_, productpro1_.id as id1_7_1_, productpro1_.id_in_category as id_in_ca2_7_1_, productpro1_.parent_id as parent_i3_7_1_, productpro1_.parent_id_in_category as parent_i4_7_1_, productpro1_.property_name as property5_7_1_, productpro1_.property_value as property6_7_1_ from product_category_property_simple_properties simpleprop0_ inner join product_property_simple productpro1_ on simpleprop0_.simple_properties_id=productpro1_.id where simpleprop0_.product_category_property_entity_id=?
Hibernate: select advancedpr0_.product_category_property_entity_id as product_1_5_0_, advancedpr0_.advanced_properties_id as advanced2_5_0_, productpro1_.id as id1_7_1_, productpro1_.id_in_category as id_in_ca2_7_1_, productpro1_.parent_id as parent_i3_7_1_, productpro1_.parent_id_in_category as parent_i4_7_1_, productpro1_.property_name as property5_7_1_, productpro1_.property_value as property6_7_1_ from product_category_property_advanced_properties advancedpr0_ inner join product_property_simple productpro1_ on advancedpr0_.advanced_properties_id=productpro1_.id where advancedpr0_.product_category_property_entity_id=?

I know that it's happens via @OneToOne, but I can't seek solution ;(

I need receive only some fields like: productName, description, productModel, productArticul, productCount...

Thanks for help, guys!

1 Answers1

1

@OneToOne is tricky if you allow null value.

JPA must try to access the record form the other table even if the property is signed with lazy-initialization, because it has to decide whether to set the property to null or a proxy-object.

It cannot set it null automatically, because there can be an associated object. It cannot create the proxy-object automatically either, because there is no designated column in the table for the id of that property (Because one-to-one relationship is mapped by shared PK by default).

So the only way to decide if the associated entity does exist is trying to load its id from it's own table. If this query returns null then no associated entity, otherwise the JPA can create a proxy object from this id.

If you really need to avoid this extra request then add a foreign key to product_category_property table into the product table.

@OneToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE], orphanRemoval = false, fetch = FetchType.LAZY)
@JoinColumn(name='product_category_property')
var productCategoryProperty: ProductCategoryPropertyEntity = ProductCategoryPropertyEntity()

You can find further explanations in this topic.

Selindek
  • 2,804
  • 1
  • 14
  • 20