How to make IDs non-sequencial? - spring-boot

I have an entity
#Entity
data class Person (
#Id #GeneratedValue
val id: Long
)
But I noticed the values for id are consecutive. Is there a way to let Spring Boot make them more random?

You can create a custom identifier generator and use it.
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
#GenericGenerator(
name = "seq",
strategy = "com.java.generators.SequenceIdGenerator",
parameters = {...})
private String id;
...
}
Here you should create com.java.generators.SequenceIdGenerator by own
A good tutorial about this

Related

Creating subcategories in kotlin spring boot

I need to implement categories and subcategories within my entities. Here's what I have so far and think it should be:
StockCategory.kt
#Entity
#Table(name = "table_categories")
data class StockCategory(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
val id: Long? = null,
#ManyToOne
#JoinColumn(name = "parentid")
val parent: StockCategory? = null,
#ManyToMany(mappedBy = "categories")
var stockItems: MutableList<StockItem> = mutableListOf(),
#OneToMany(mappedBy = "parent")
var childCategories: MutableList<StockCategory> = mutableListOf(),
)
StockItem.kt
#Entity
#Table(name = "table_stock")
data class StockItem(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "stock_item_id")
val id: Long? = null,
#Column(name = "stock_item_name")
var name: String = "New Item",
...
#ManyToMany
#JoinColumn(name = "item_category", referencedColumnName = "category_id")
var categories: MutableList<StockCategory> = mutableListOf(),
...
)
Now at the moment, this looks to be correct... At the very least Spring Boot is not complaining.
However, in terms of what to do next, I'm not sure. I know I need to implement a JpaRepository, of which I have the current:
StockCategoryRepository.kt
interface StockCategoryRepository: JpaRepository<StockCategory, Long> {
}
I also need to implement the relevant methods in my service class.
What exactly do I need to do next in order to get this to work and be able to use the information later on? Please also ELI5 too as although I have a decent amount of knowledge on this, I'm still not where I would like to be when it comes to this.
A few background bits if it makes it easier for you.
I'm using H2 as my database, Spring Boot and Kotlin as my language.

findBy not working with inherited properties

I have the following model and repository:
#Entity
#Table(name = "db_user", uniqueConstraints = { #UniqueConstraint(columnNames = "email") })
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_user")
#SequenceGenerator(name = "seq_user", sequenceName = "seq_user")
#Column(name = "id")
private Long id;
// ...
}
#Entity
#Table(name = "movie")
public class Movie extends AbstractItem {
// Id column inherited from AbstractItem
// ...
}
#Entity
#Table(name = "movie_user")
public class MovieOwnership extends AbstractOwnership {
#ManyToOne
private Movie movie;
// ...
}
#MappedSuperclass
public abstract class AbstractOwnership{
#Id
#SequenceGenerator(name = "seq_default", sequenceName = "seq_default")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_default")
#Column(name = "id")
private Long id;
#ManyToOne
private User owner;
// ...
}
public interface MovieOwnershipRepository extends QueryDslJpaRepository<MovieOwnership, Long> {
List<MovieOwnership> findByOwnerId(Long ownerId);
MovieOwnership findByOwnerIdAndMovie(Long ownerId, Movie movieId);
List<MovieOwnership> findByOwnerIdAndMovieIdIn(Long ownerId, Set<Long> movieIds);
}
I'm trying to use Spring's findBy requests to fetch MovieOwnerships by owner or movie, using the id field of both entities. I'm able to work directly with the owner's id, but using MovieId in my requests seems broken (I can use the whole Movie object though). In the code above, the first two findBy are fine but the last one throws this exception:
Caused by: java.lang.IllegalArgumentException: Unable to locate
Attribute with the the given name [movieId] on this ManagedType
[carrm.app.data.AbstractOwnership]
It compiles if I try with another property from Movie (like findByMovieTitle), but I can't make it work on the id.
Any idea how to solve this?
I tried the same with JpaRepository instead of QueryDslJpaRepository.
The SQL is generated correctly:
select movieowner0_.id as id1_1_, movieowner0_.owner_id as owner_id2_1_, movieowner0_.movie_id as movie_id3_1_
from movie_ownership movieowner0_
left outer join user user1_ on movieowner0_.owner_id=user1_.id
left outer join movie movie2_ on movieowner0_.movie_id=movie2_.id
where user1_.id=? and (movie2_.id in (?))
So it must be a QueryDslJpaRepository implementation bug.
I would suggest you use JpaRepository instead.

Is possible to create an entity with two primary key values?

I'm having a problem with one Entity which contains an unique_key so perhaps what I need is to create that Entity with two PrimaryKey is that even possible?
This is my Entity
#Entity
public class UserAnswerQuestion extends DateAudit {
#Id
#Column(name = "useranswerquestion_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "useranswerquestion_seq")
#SequenceGenerator(name = "useranswerquestion_seq", allocationSize = 1)
private Long id;
#ManyToOne
private User user;
#ElementCollection
private List<Answer> answerList;
#ManyToOne
private Question question;
private Boolean passed;
private Boolean shown;
public UserAnswerQuestion(){
}
....
And the problem when I try to create this Entity with another User it says :
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "uk_6v3tlg1gflua8r8d4wlqxo7v5"
Detail: Key (answer_list_answer_id)=(11) already exists.
So what I'd like to do is make User as a #Id if possible, and maybe it solves my problem...
EDIT
What I did is, create a class UserAnswerQuestionId like this :
public class UserAnswerQuestionId implements Serializable {
#Column(name = "useranswerquestion_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "useranswerquestion_seq")
#SequenceGenerator(name = "useranswerquestion_seq", allocationSize = 1)
private Long id;
#Column(name ="user_id")
private Long user_id;
public UserAnswerQuestionId(){
}
public UserAnswerQuestionId(Long user_id) {
this.user_id = user_id;
}
And then in the UserAnswerQuestion entity I changed it to :
#EmbeddedId
private UserAnswerQuestionId userAnswerQuestionId;
But the error now says :
org.hibernate.id.IdentifierGenerationException: null id generated for:class com.pew.model.useranswers.UserAnswerQuestion
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:800) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:785) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
Edit 2
I'm reading carefully the error and looks like the problem is in the
#ElementCollection
private List<Answer> answerList;
How do I solve this to allow this element repeat on this Entity?
Perhaps I can define those #ElementCollection not to be Unique? so can be repeated on this Entity?
This is my Answer Entity
#Entity(name = "answer")
public class Answer extends DateAudit {
#Id
#Column(name = "answer_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "answer_seq")
#SequenceGenerator(name = "answer_seq", allocationSize = 1)
private Long id;
#Column(name = "answerToQuestion")
private String answerToQuestion;
Perhaps you may need to explain your usecase better. From contextual info, looks like you want to retrive Users and the Answers they provided for various questions.In that case you may just need an association table like below.
public class User extends DateAudit { //This assumes you are intested in retrieving User and their answer(s) in the domain model.
#Id
private Long id; //userid.
#ManyToMany
#JoinTable(name = “ANSWERS_FOR_QUESTIONS”, //More appropriate name may be : ANSWERS_BY_USERS
joinColumns = { #JoinColumn(name = “user_id”) }, //fk
inverseJoinColumns = { #JoinColumn(name = “question_id”) }) //fk
private Set<Answers> answers = new HashSet<Answers>();
Primary key is always only 1 and unique. It can be composite key made of multiple columns tuple.
Including your UserAnswerQuestionId by using #IdClass should work:
#Entity
#IdClass(UserAnswerQuestionId.class)
public class UserAnswerQuestion extends DateAudit {
#Id
private Long id;
#Id
private Long user_id;
#ElementCollection
private List<Answer> answerList;
#ManyToOne
private Question question;
private Boolean passed;
private Boolean shown;
public UserAnswerQuestion(){
}

Mapping nested collections with hibernate JPA annotations? Map<Key,Set<Values>>

I'm not sure how to annotate the bellow classes correctly. What I'm trying to do is to have on the User entity, a list of roles for a mapped Asset.
#Entity
class Asset{
#Id #GeneratedValue(strategy = GenerationType.AUTO)
var id: Long = -1
}
#Entity
class Role {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
var id: Long = -1
}
#Entity
class User{
#Id #GeneratedValue(strategy = GenerationType.AUTO)
var id: Long = -1
???
var roles: Map<Asset, Set<Role>> = HashMap()
}
From what I discovered hibernate does not support a map of sets (link here). So I tried other options as an intermediate class like this.
But I'm struggling with the annotations. Can someone please tell what annotations I have to put on the three classes?
If someone knows a better approach instead of the map, to have the user roles by asset, please tell.

How to use a custom SequenceGenerator for Hibernate in Spring for all entities?

I've implemented a custom SequenceGenerator that I want to use in all my entities for the "id". But rather than having to do something like this for each entity:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XyzIdGenerator")
#GenericGenerator(name = "XyzIdGenerator",
strategy = "com.mycompany.myapp.id.BigIntegerSequenceGenerator",
parameters = {
#Parameter(name = "sequence", value = "xyz_id_sequence")
})
public BigInteger getId()
{
return id;
}
is there a way to apply this SequenceGenerator to ALL entities by default using vanilla Hibernate/JPA or perhaps by using Spring?
Just move the code segment to a super class, add add #MappedSuperclass to it. But, in that case, all your entity will use the same seq generator
#MappedSuperclass
public class SeqIdable implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XyzIdGenerator")
#GenericGenerator(
name = "XyzIdGenerator",
strategy = "com.mycompany.myapp.id.BigIntegerSequenceGenerator",
parameters = {
#Parameter(name = "sequence", value = "xyz_id_sequence")
})
public BigInteger getId() {
return id;
}
}

Resources