I am using Spring with JPA and have 2 entities:
public class DocumentType {
#Id
#GeneratedValue
private Long id;
private String name;
private String idPrefix;
private Integer maxNumberOfSuffixDigits;
private Long startingSuffixNumber;
private String nextDocId;
...
}
public class Document {
#Id
private String id;
#ManyToOne
#JoinColumn
private DocumentType documentType;
...
}
Many Documents can be mapped to the same DocumentType, and there can be many DocumentTypes. The id for the Document will be generated using the combination of parameters found in it's corresponding DocumentType which is: idPrefix + startingSuffixNumber prefixed with 0s to meet the maxNumberOfSuffixDigits constraint. Currently I am on the fence of whether or not I will store the pre-generated nextDocId, but for the purposes of this question (if it makes the solution easier) let's assume that it is available.
The problem I have is getting the nextDocId, and generating the following Id in a concurrent environment. I've looked into Pessimistic write locks, but I do not think that can solve this issue, as from what I've seen, it only locks a single query. I need a way to somehow lock two queries: select query on the nextDocId, followed immediately by an update to generate a new nextDocId.
Any suggestions on how I can achieve this, or other alternatives to this problem would be greatly appreciated.
Related
So I've read tons and tons of posts, forums, question and answers about this topic but I am still quite unsure about what migration means.
Let me first introduce you to my problem.
I have a Spring Boot backend working with MySQL Database with the help of Spring JPA Entities.
Now my problem is the following:
I already have a Spring JPA Entity defined as follows:
#Entity
#Getter
#Setter
public class Blog {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String content;
private LocalDateTime createdAt = LocalDateTime.now();
}
After having this entity persisted to the database, I realized that the simple String title wouldn't be enough as it maps to a varchar(255) making my Blog posts couldn't exceed 255 characters.
So I would like to change this in the database without running any custom built SQL scripts to modify the column type in the table.
I have something in mind like I would modify the current JPA Entity as follows:
#Entity
#Getter
#Setter
public class Blog {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
#Lob // <--- Notice the Lob here
private String content;
private LocalDateTime createdAt = LocalDateTime.now();
}
If I am right, the Large Object annotation would mean that in the Database the column type becomes longtext allowing a lot more characters than 255 so my blog posts would fit.
So in short this is how I stumbled upon the topic of Database Migration I am just unsure if that is the thing I need in this situation. Can you please confirm if I am right about this, also can you please suggest possible solutions for this particular problem? I know there is a tool called Liquibase used for migrations which is quite popular. Should I go for it, or is there somthing else I would need?
Thanks in advance for the help.
Within JPA in Spring Boot / Spring Data, I want to set up an entity class.
The business requirement is to historize the processing of documents that exist once per year each.
The processing is performed in a bulk: so all documents are processed together: there is one processingTimestamp for all documents in the database for each processing sequence.
Later on, I want to access only the most recently processed document, but keep the previously processed documents for reference.
I see the following alternatives:
Use a composite key
#Id
private String documentId;
#Id
private String yearOfDocumentCreation;
#Id
private java.sql.Timestamp processingTimestamp;
Use an auto generated key
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String documentId;
private String yearOfDocumentCreation
private java.sql.Timestamp processingTimestamp;
Which alternative is better/best practise regarding
Handling (e.g. storing a list of documents as a bulk that were read before from the database and avoiding duplicates in the database)
Performance
Or do I miss other alternatives/aspects?
I recommend using a single Long primary key, if you will need to make a foreign key to this table.
To avoid dublicates you can make a unique constraint on the 3 required columns.
I have an indexed entity, like below :
#MappedSuperclass
public static class Model{
#Id
#GeneratedValue(strategy=GenerationType.Identity)
private Integer id;
private boolean isDeleted;
}
and the indexed class is :
#Entity
#Table("USERS")
#Indexed
public class ProductModel extends Model{
#Field
private String name;
//getters & setters
}
Well, when I do a research on ProductModel, I get the value of the flag isDeleted while its not annotated with #Field.
I'm asking if this is a normal behavior, does Hibernate-search Index the whole Object Or does it fetch the missing data from data base, I need an explanation for this behavior please.
Hibernate Search only stores in the index the fields you declare explicitly (more precisely, it indexes by default and you can ask Hibernate Search to store it by adding the store option to your #Field annotation).
What you observe is that Hibernate Search hydrates the objects with the information of the database after having performed the search. This is one of the main interest in using Hibernate Search: the objects returned are managed entities.
Say I have these classes:
public class Loan {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "lender_id")
private User lender;
}
public class User {
#Id
private Long id;
#Column
private String userName;
#OneToMany
private List<Loan> loans;
}
Now, let's say I have the user (lender) id and in the DAO layer, I want to create a Loan based on the id of the lender?
I realize that I can do the following:
User u = userDao.getUserById(1234L);
loanDao.createLoan(u, "someLoan");
But I'm wondering if it's possible to do it without pre-loading the User record?
There isn't a good way to do that, in part because it would fundamentally lead to incorrect ORM code. You the programmer are responsible for managing the in memory state of the Entities and keeping them correct. If you create a new Loan and say it belongs to a User, and a User has a collection of Loans, it is your responsibility to add that Loan to the User! (This has real consequences as soon as the caches get involved.)
You're using ORM, you need to think in terms of the objects and not in terms of the database. Adding a number in a foreign key column isn't what's important, setting up the correct in-memory representation of the object Model is what's important for you. The database is hibernate's problem.
I have an Keyword and a KeywordType as entities. There are lots of keywords of few types. When trying to persist the second keyword of a type, the unique constraint is violated and the transaction is rolled back. Searching SO i found several possibilies (some of them from different contexts, so I'm not sure of their validity here) - this post and this post advise catching the Exception which would be of no use to me as I end up where I started and still need to somehow persist the keyword.
Same applies to locking proposed for a different situaltion here Custom insert statements as proposed in this and this posts wouldn't work proper I guess, since I'm using Oracle and not MySQL and woulnd like to tie the implementation to Hibernate. A different workaround would be trying to retrieve the type first in the code generating the keywords, and set it on the keyword if found or create a new one if not.
So, what would be the best - most robust, portable (for different databases and persistence providers) and sane approach here?
Thank you.
The involved entities:
public class Keyword {
#Id
#GeneratedValue
private long id;
#Column(name = "VALUE")
private String value;
#ManyToOne
#JoinColumn(name = "TYPE_ID")
private KeywordType type;
...
}
and
#Entity
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = { "TYPE" }) })
public class KeywordType {
#Id
#GeneratedValue
private long id;
#Column(name = "TYPE")
private String type;
...
}
Your last solution is the right one, IMO. Search for the keyword type, and if not found, create it.
Catching the exception is not a good option because
it's hard to know which exception to catch and make your code portable across JPA and DB engines
The JPA engine will be in an undetermined state after such an exception, and you should always rollback in this case.
Note however that with this technique, you might still have two transactions searching for the same type in parallel, and then try to insert it in parallel. One of the transaction will rollback, but it will be much less frequent.
If you're using EJB 3.1 and you don't mind serializing this operation, a singleton bean using container managed concurrency can solve the problem.
#Singleton
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class KeywordTypeManager
{
#Lock(LockType.WRITE)
public void upsert(KeywordType keywordType)
{
// Only one thread can execute this at a time.
// Your implementation here:
// ...
}
#Inject
private KeywordTypeDao keywordTypeDao;
}
I would go for this option:
A different workaround would be trying
to retrieve the type first in the code
generating the keywords, and set it on
the keyword if found or create a new
one if not.