Hi. I'm trying to design a nice API around entitie...
# announcements
c
Hi. I'm trying to design a nice API around entities in a kotlin project with JPA. In a legacy project we have been plagued with
Copy code
val entity =  repository.getOne(entityId)
val entityId = checkNotNull(entity.id) { "Entity id cannot be null if it is retrieved from the database" }
all over the place. In the new project first we thought of using contracts, but their restrictions make them useless for this case, so this more crude idea was born:
Copy code
abstract class BaseEntity<T : Serializable> {
	/**
	 * Direct use of `_dbId` is strongly discouraged in business level code, this should only be used on the persistence
	 * level by the framework and related infrastructure code.
	 */
	@Suppress("PropertyName")
	protected abstract val _dbId: T?
	/**
	 * Accessing `id` implies that entity has been retrieved from a repository or crafted with a pre-set identifier.
	 * If this condition is not met, accessing it will generate an `IllegalStateException`.
	 */
	@get:Transient
	val id: T
		get() = checkNotNull(_dbId) { "Entity has not been saved" }
}
Any thoughts? Improvements? Maybe better alternatives? In this here case, implementation would look like:
Copy code
@Entity(name = "clients")
class InfoEntity(
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	override val _dbId: Long? = null,
	@Column(name = "first_name")
	var info: String = ""
) : BaseEntity<Long>()
m
So optimally you would want
id
to be inaccessable before saving it to DB, and then non-nullable after. Right?
c
Ideally, yes, our `protected abstract _dbId`/implemented
id
solution does not give us the compile time checking (like more advanced contracts would), but at least it reduces boilerplate. Plus to be honest people rarely need id of an unsaved entity anyway, so the chance that someone would try to access it is pretty slim and will probably be caught by the test suite pretty soon.
i
@Czar you could do your own property delegate which does all the checks under the hood
c
Maybe for the legacy code, but here why would I add that complexity if I can implement the check in the class itself and not have to Alt+enter+import all the time? 🙂
Speaking of delegates, what I wonder is, what's better, what I have above with custom getter, or maybe this:
Copy code
@get:Transient
	val id: T by lazy { checkNotNull(_dbId) { "Entity has not been saved" } }
a
I don't know if http://oneeyedmen.com/nothing-can-save-us.html is something that could help you and which you would want to use?