possible alternatives to #GeneratedValue with #EmbeddedId in JPA - java

My entity has a wrapped Identifier like this,
#Entity
public class Article {
#EmbeddedId
private ArticleId articleId;
....
}
#Embeddable
public class ArticleId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
in my architecture, multiple application instances(it all same.) are connected to the same data source
so #GeneratedValue(strategy = GenerationType.IDENTITY)seems good
because even if instances A and B try to create Accountat the same time, Its Id is guaranteed by the database.
problem is that #GeneratedValue is only able to use with #Id annotation (#Id is not available for EmbeddedId)
PersistenceUnitUtil.getIdentifier(Object entity) could be a alternative? like this,
ArticleId articleId = ArticleRepository.nextIdentity();
I am not sure that whether it causes the race condition.
Could PersistenceUnitUtil.getIdentifier(Object entity) guarantee the unique id across the different application instance(JVM)? I don't think so.
In this situation, What alternative is possible?

One solution could be to use an #IdClass to get rid of the nested property and be able to generate the identifier (since nested properties are "assigned" and cannot be generated, so the call of PersistenceUnitUtil.getIdentifier(Object entity) would not help here). See e.g. here for a complete guide (also linked in the linked answer by #SternK from the comment)
An #IdClass could look like this:
public class ArticleId implements Serializable {
private Long id;
}
An entity could use it:
#Entity
#IdClass(ArticleId.class) // specified dependency
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
// expose "typed" id:
public ArticleId getId() {
return new ArticleId(id);
}
}
Spring-Data #Repositorys also work with corresponding #IdClass objects, e.g.:
#Repository
public interface UserEntityRepository extends JpaRepository<Article, ArticleId> {
}
// would offer e.g. this method:
repository.findById(new ArticleId(123L));

Related

Diffrence between _Id and Id in JPA

What's the difference between those two lines in JPA
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByUser_Id(Long id);
}
And
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByUserId(Long id);
}
So i edited my question to be more clearly and get a good response so there is my domain classes User and Post and i have relation #ManyToOne in Post class.
Domain
...
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
And
...
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
If you are using Spring Data JPA then class is a Spring JPA repository in which the implementation is done by the Spring Framework and the method name is used for construction of the query.
Check 2.3.2 Query creation for how the query will be constructed.
So, in your case List<Post> findByUser_Id(Long id); method might throw error on runtime since it's not in valid method format.
Its just the name of the method. Other than that, there is no difference between those lines.

Eradicate boilerplate

public class Toponym {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(columnDefinition="TEXT default ''", nullable = false)
public String name;
}
public class LevelOneEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(columnDefinition = "boolean default false", nullable = false)
private boolean archived;
}
public class LevelTwoEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(columnDefinition = "boolean default false", nullable = false)
private boolean archived;
}
These two classes definitely have some boilerplate code. If multiple inheritance were a reality, I'd organize two mixins here: IdMixin and ArchivedMixin. Therefore classes would contain no bodies at all. But in Java it is not possible.
It may be possible to use multiple interfaces but they can't contain the code itself if I'm not mistaken.
How to cope with such a problem in Java?
If those annotations are allowed on methods, not only on fields (e.g. getters or setters), you can declare them in interfaces and implement as many interfaces as you want. The fields will still be declared in each derived class, though, but you won't need to restate the annotations for them.
Otherwise, you'll have to create a class hierarchy that allows for the flexibility you need:
class Entity // contains annotated ID field and declares the generic ID type
class ArchivedEntity extends Entity // if you don't expect to have non-entity archived classes (i.e. archived objects with no ID)
... etc
This can get pretty complex as you add more combinations.

How to provide Initial value OR Increment ID with JPA GenerationType.AUTO

I am using following code to define MyEntity,
#Entity
#Table(name = "MY_TABLE")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "MY_TABLE_ID")
private Integer myTableId;
#Column(name = "MY_TABLE_NM")
private String myTableName;
//Getters Setters
}
For the first POST after my application starts, I create MyEntity everything works fine, MY_TABLE_ID starts with 1 and works as expected.
My issue is, If somebody inserts data manually before I do my POST then I get duplicate key exception as myTableId is entered as 1 which is already present.
My main problem is I can't create database sequence for using GenerationType.SEQUENCE now to resolve this as database can't be altered now.
I have tried various combinations of GenerationType, TableGenerator but I am unable to successfully tackle it.
Setting initialValue to some larger number to avoid duplicate values can temporarily resolve my problem but I am unable to do it too.
If someone can help me with initialValue with AUTO or give me some other better solution without database changes will be great :)
As MY_TABLE_ID is an identity column, following annotations will work.
#Entity
#Table(name = "MY_TABLE")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY) // <-- IDENTITY instead of AUTO
#Column(name = "MY_TABLE_ID")
private Integer myTableId;
#Column(name = "MY_TABLE_NM")
private String myTableName;
//Getters Setters
}
The identity column will automatically assign an value as soon as the transaction is committed. You are not to set any values for an identity column, as its the job of the database to assign the values. Therefore you also don't need to think about any initial values (forget them completely for identity columns)
I tried various options in answers provided here and for similar questions on stackoverflow and other forums,
I had few limitations,
I couldn't create database sequence as my database changes were freezed.
I didn't want to introduce new Custom IdGenerator class because it would add confusion to other people working with me.
It was resolved using following change:
Adding GenericGenerator with increment strategy helped me, I made following changes to my code.
#Entity
#Table(name = "MY_TABLE")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator="seq")
#GenericGenerator(name = "seq", strategy="increment")
#Column(name = "MY_TABLE_ID")
private Integer myTableId;
#Column(name = "MY_TABLE_NM")
private String myTableName;
//Getters Setters
}
It helped me because,
From Hiberbate DOCs
increment
An IdentifierGenerator that returns a long, constructed by counting
from the maximum primary key value at startup. Not safe for use in a
cluster!
Since, it was incrementing already existing myTableId even if it was manually inserted, this resolved my issue.
You can also implement your own generator if you need more control.
See this interface IdentifierGenerator.
So you can get the count of records, for example through a #NamedQuery.
Then you can generate an identifier yourself.
public class MyEntityKeyGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SessionImplementor session, Object object) {
// SELECT count(ent) from MyEntity ent;
Long count = (Long) session.getNamedQuery("count-query").uniqueResult();
// calc and return id value
}
}
Entity:
class MyEntity {
#Id
#GenericGenerator(name = "my_generator",
strategy = "org.common.MyEntityKeyGenerator")
#GeneratedValue(generator = "my_generator")
private Long id;...
Just do not forget about the lock.
I use the generation type Identity, which basically means that the db, takes care of Id generation.
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#MappedSuperclass
#EntityListeners(EntityListener.class)
#EqualsAndHashCode(of = {"id", "createdAt"})
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private ID id;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED_AT", updatable = false)
private Date createdAt;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "UPDATED_AT")
private Date updatedAt;
}
You can also use, Sequence generation:
#Entity
#SequenceGenerator(name="seq", initialValue=1, allocationSize=100)
public class EntityWithSequenceId {
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
#Id long id;
}

Hibernate - Use of #OneToMany or #ManyToMany targeting an unmapped class: com.podro.model.Journey.roadWay[com.podro.model.RoadElement]

I saw similar questions, but answers weren't helpful. So, i get this error:
Use of #OneToMany or #ManyToMany targeting an unmapped class: com.podro.model.Journey.roadWay[com.podro.model.RoadElement]
I'm trying to create List with objects of RoadElements (which is interface for class Point and Section). There is any other way to do it? From what i know, i guess that is the only way to create proper mapping for this classes, and have list of this elements.
#Entity
#Table(name="Journey")
public class Journey {
// Some other fields
#Column(name="road_way")
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private List<RoadElement> roadWay;
}
#MappedSuperclass
public interface RoadElement {}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Point")
public class Point implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String name;
#Column(name="time_in_days")
private int timeInDays;
private Rate rating;
}
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name="Section")
public class Section implements RoadElement{
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name="section_name" , length=100)
private String sectionName;
#Column(name="time_in_days")
private int timeInDays;
#Column(name="kind_of_transport")
private Locomotion kindOfTransport;
}
Thanks for answers, I would be very grateful for help!
Associations are between entities. RoadElement is not an entity. It's an interface.
You may not do what you're trying to do. Hibernate needs to know the type of the entities contained in roadWay.
So, RoadElement should be a class, annotated with #Entity, having an ID that uniquely identifies a RoadElement among all the road elements (sections, points, etc.)
Section and Point should extend from RoadElement, and should NOT have their own ID, since it's inherited from RoadElement.

Alternative types of keys for sub entity

I'm using Jpa with GAE. I have an Entity with collection of sub Entity as listed below.
In Entity A I'm using long as Id, In B I'm using Key as Id.
Now evreything works fine except that I need to import 8MB JAR file appengine-api.jar to my android app just for the Key class.
I tried extracting the jar and take only Key.class but it was messy because I had to add more classes that Key.class is using.
Is there another type of key that I can use?
#Entity
public class A implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int a;
#OneToMany
#Basic
private List<B> bList;
.
.
}
#Entity
public class B implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
int b;
.
.
}
Unfortunately not, Key is the only class of this kind that is available. It has no superclass or meaningful interface you may use instead.

Categories