Hibernate - Insert generated id in column other than id one on save - java

is there a way to perform insertion of an #Id-column value into a non #Id column when entity is being created/updated to avoid double-save-ing?
#Entity
class DummyEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "dummy_id_generator")
#SequenceGenerator(name = "dummy_id_generator", sequenceName = "dummy_id_seq")
Long id;
Long rootId;
}
#Repository
class DummyEntityRepo implements JpaRepository<DummyEntity, Long> {}
#Service
class DummyEntityService {
#Autowired
DummyEntityRepo repo;
void save(DummyEntityVO newDummyEntity) {
var newEntity = newDummyEntity.mapToEntity();
// !!! double save here !!!
repo.save(newEntity);
if(newEntity.getRootId() == null) {
newEntity.setRootId(newEntity.getId());
repo.save(newEntity);
}
}
}
What I was thinking was, since #Id is based on a sequence, I can try extracting next value from it and setting it as id and rootId before saving
Long nextId = // get next id somehow
newEntity.setId(newId);
if (newEntity.getRootId()) {
newEntity.setRootId(nextId);
}
repo.save(newEntity);
However:
I don't like perspective of interfering with Hibernate's id acquisition & saving methods
I found package org.springframework.jdbc.support.incrementer but unfortunately there is no sequence based OOB incrementer for my DB so it would require a custom implementation (found something promising here though)

#GeneratorType allows to generate/set values into entity during insertion phase. Suppose your entity looks like this:
#Entity
class DummyEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "dummy_id_generator")
#SequenceGenerator(name = "dummy_id_generator", sequenceName = "dummy_id_seq")
Long id;
#GeneratorType(type = RecordIdGenerator.class, when = GenerationTime.INSERT)
Long rootId;
}
And your RecordIdGenerator class looks like:
public class RecordIdGenerator implements ValueGenerator<Long> {
#Override
public Long generateValue(Session session, Object owner) {
return ((DummyEntity)owner).getId();
}
}
Then when you'll insert new record hibernate will generate similar queries:
Hibernate: select nextval ('dummy_id_seq')
Hibernate: insert into dummy (record_id, id) values (?, ?)
and record_id will be equal to id

Related

#GeneratedValue seemingly does not auto-generate when the Entity is being added using a POST Request

This is my Entity class (shortened for obvious reasons):
Song.java:
#Entity
#Table(name = "songs", schema = "dbo")
public class Song {
#Id
#GeneratedValue
#Column(name = "song_id")
private Integer songId;
// other properties and getter setters not needed for the question
}
Here's my controller (again, shortened for obvious reasons):
SongsController.java:
#RestController
#RequestMapping("/songs")
public class SongsController {
#Autowired
private SongRepository songs;
//GET, DELETE and PUT mappings not neeeded for the question
//POST: adds a new song to the repository
#PostMapping("/add")
public void addSong(#RequestBody(required = true) Song song) throws DuplicateItemException {
if(songs.existsById(song.getSongId())) {
throw new DuplicateItemException(); //simplest possible custom exception handler imaginable
}
songs.save(song);
}
}
Here's my POST request from POSTMAN:
{
"songName": "Song3",
"songDuration": "490"
}
Here's what the function receives:
songName: "Song3"
songDuration: "490"
songId: null
The Exception that gets thrown is "songId cannot be null". How do I fix this?
My database is PostgreSQL (12.10.1).
You need to explicitly add strategy to generated value. Otherwise it will not work. Try this:
#GeneratedValue(strategy = GenerationType.AUTO)
Your #GeneratedValue is missing how to generate the value! Given that you're using PostgreSQL and such RDMS allows the creation of database sequences, I'll suggest the following configuration ... In your Song class, you need to add the #SequenceGenerator annotation and make the following changes:
#Entity
#Table(name = "songs", schema = "dbo")
#SequenceGenerator(
name = "<AnyNameThatFitsYou>",
sequenceName = "<TheNameOfYourDatabaseSequence>",
initialValue = <DatabaseSeqInitVal>,
allocationSize = <HowManyOfTheseValuesShouldBeAllocatedInJPAMemoryForFastAccess>
)
public class Song {
#Id
#GeneratedValue(
generator = "<TheNameYouGiveToTheSequenceGenerator>",
strategy = GenerationType.SEQUENCE
)
#Column(name = "song_id")
private Integer songId;
// other properties and getter setters not needed for the question
}
That should be all ...

Java Entity: Either manually setId or autogenerate from sequence

I have an entity with a composite key using #IdClass as following:
#Entity
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#JsonIgnoreProperties(ignoreUnknown = true)
#IdClass(MyId.class)
#SequenceGenerator(
name = "My_SEQUENCE",
initialValue = 1,
allocationSize = 1
)
public class MyEntity implements Serializable {
#Id
#Column(name = "id")
#GenericGenerator(
name = "MY_SEQUENCE",
strategy = "StringPrefixedSequenceIdGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = StringPrefixedSequenceIdGenerator.INCREMENT_PARAM, value = "1"),
#org.hibernate.annotations.Parameter(name = StringPrefixedSequenceIdGenerator.VALUE_PREFIX_PARAMETER, value = "ABC"),
#org.hibernate.annotations.Parameter(name = StringPrefixedSequenceIdGenerator.NUMBER_FORMAT_PARAMETER, value = "%07d") })
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "MY_SEQUENCE")
private String reqId;
#Id
#Column(name = "rev_no")
private Long revisionNumber;
...
}
And, it's repository as:
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
}
And on my service I want following:
public class MyEntityService extends BaseService<MyEntityRepository, MyEntity, Long> {
#Transactional
public MyEntity create(Payload payload) {
...
myEntity.setRevisionNumber(1);
myEntityRepository.save(myEntity);
...
}
public MyEntity update(String reqId, Long revisionNumber, Payload payload) {
var myEntity = findByIdAndRevisionNumber(reqId, revisionNumber);
...
myEntity.setRevisionNumber(revisionNumber + 1);
myEntity.setReqId(reqId);
myEntityRepository.save(myEntity);
}
I have two cases:
On create, Auto-generate reqId using sequence defined in Entity.
On update, increment revision number manually and save new entity with the same reqId.
For case 1, it's working fine but on case#2 it's overriding reqId with auto-generated sequence.
Is there any way to setId manually so that generator won't override it?
Since you are using GenerationType.IDENTITY, you won't be able to use setters for particular attribute. It won't work.

Spring data JPA only one composite key is auto incremented issue

I am using MySQL database.
In my table i there are two four primary keys, out of which one is auto incremented.
#Embeddable
public class EmployeeId implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "id", nullable = false)//
This is just Pk in mysql table
**private int id;**
// I have tried and #GeneratedValue(strategy = GenerationType.IDENTITY),
#GeneratedValue(strategy = GenerationType.IDENTITY)
//and #GeneratedValue(strategy = GenerationType.TABLE)
//#GeneratedValue(strategy = GenerationType.AUTO, generator = "id") #SequenceGenerator(name = "id", sequenceName = "id")
**this is auto incremented and pk in mysql table**
#Column(name = "gender_key", nullable = false)
private int gender_key;
}
#Entity
#Table(name = "employee")
public class employee {
#EmbeddedId
private EmployeeId employeeId;
private String emp_name;
private String mobile_no;
employee() {
}}
public interface employeeRepository extends
JpaRepository<employee, EmployeeId> {
}
In My Controller I want id after employeeRepository.save(bean); method because i want to save that id in different db .
logger.info("gender_key is --- > "+gender_key);
But I am getting always 0 value of gender_key.
The thing which I have tried is:
bean = employeeRepository.save(bean)
int gender_key= bean.getGender_key();
logger.info("gender_keyis --- > "+gender_key);
But still the value for gender_key is 0(Zero).
Or any Query which I have to write in repository .
How I can get the auto incremented value of gender_key which is inserted into MySQL table?
Please Help.
Thanks in advance.
Your JPA #Id does not need to match the database PK column(s). So long as it is unique then that is all that matters.
From https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing:
The JPA Id does not always have to match the database table primary
key constraint, nor is a primary key or a unique constraint required.
As your an auto-increment column is guaranteed to be unique then just use gender_key as your #ID and map id as a normal column.
#Entity
#Table(name = "employee")
public class employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int genderKey;
#Column
private int id;
}
To be honest I would find your schema confusing however.
I would also suggest reading the following:
https://www.javatpoint.com/java-naming-conventions
You're missing a #GeneratedValue at the id field.
Depending on its values, you're free to choose a strategy for generation, like a sequences, id tables, an automatic internal id generation.
Last but not least GenerationType.AUTO will choose one of the mentioned strategies.
See the Javadocs for javax.persistence.GeneratedValue and javax.persistence.GenerationType.

JPA Sequence Generated Value Not Working

I need to use an existing sequence of the db to generate the id of this entity:
#Entity
#Table(schema="sistema", name="clientes_consultas")
public class Consulta implements Serializable {
#Id
#SequenceGenerator(schema = "sistema", sequenceName = "clientes_consultas_id_seq",
name = "seq_c", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_c")
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
This is my sequence, and its stored on a schema that is not the postgres default 'public', its called 'sistema':
Unfortunately, theres an stacktrace saying that the sequence dont exists :(
Any clue?
Your sequence owner is "postgres". Maybe you defined different user than postgres in your connection.

JPA Join using arbitrary field (not primary key)

I've got two entities that I want to join together using a field they have in common, called shared_id. The field is not the primary key of either entity. The shared_id is unique - each Hipster will have a unique shared_id.
The tables look like:
Hipster Fixie
========= ========
id id
shared_id shared_id
There is a OneToMany relationship between Hipsters and their Fixies. I've tried something like this:
#Entity
public class Hipster {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "shared_id")
private Integer sharedId;
#OneToMany(mappedBy = "hipster")
private List<Fixie> fixies;
}
#Entity
public class Fixie {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name = "shared_id", referencedColumnName = "shared_id")
private Hipster hipster;
}
#Repository
public class HipsterDAO {
#PersistenceContext
private EntityManager entityManager;
public Hipster getHipsterBySharedId(Integer sharedId) {
String queryString = "SELECT h FROM Hipster h WHERE h.sharedId = :sharedId";
TypedQuery<Hipster> query = entityManager.createQuery(queryString, Hipster.class);
query.setParameter("sharedId", sharedId);
try {
return query.getSingleResult();
} catch (PersistenceException e) {
return null;
}
}
}
Now, my DAO gives me this error:
java.lang.IllegalArgumentException: Can not set java.lang.Integer field Hipster.sharedId to java.lang.Integer
I think it's upset because the sharedId field is used in a relation, rather than just being a basic field. I haven't included the sharedId field in the Fixie entity, but I get the same result if I do. How do I persuade it to run this query for me? Do I need to change the query or the entities?

Categories

Resources