Guarantee unique number generation with spring data and hibernate - spring

I'm developing a spring mvc webapp with spring data and hibernate.
I've an entity composed by a boolean field and by an Integer field.
At beginning the boolean field is false and Integer field is null.
When boolean field become true I need to assign to the Integer field a unique value equals to MAX(seq) +1
To do it, I write my service method in this way:
#Override
public synchronized Obj save(Obj entry) {
if (entry.getBool() && entry.getSeq() == null){
Integer seq = objRepository.getLastSeq();
if (seq == null){
seq = 1;
}
entry.setSeq(seq);
}
return entry.save(entry);
}
And in my Reposiroty:
#Query("select MAX(seq)+1 FROM Obj")
Integer getLastSeq();
I put synchronized keyword to service method in order to be sure that only a thread at a time can get an unique MAX+1 number.. but I'm not sure if it works in all situation and if it is the right way.
Can I be sure that it guarantee unicity of seq?
Thank you
Marco

It seems like your Integer is entry id or isn't it ? So why not to use database sequence, with #id adnotation for example :
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;

Example how to use initializing bean for that purpose.
#Service
public class SequenceInitializer implements InitializingBean{
#Autowired
private ObjRepository objRepository;
#Autowired
private Service service;
#Override
public void afterPropertiesSet() throws Exception {
try {
Integer max = objRepository.getLastSeq();
service.setLastSeq(max);
} catch(Exception e){...}
}
In your service setLastSeq will set AtomicInteger field.

Related

Spring Boot JPA find, filter

As Spring jpa Provides some usefull features to find Items from a repository by defining it in the method name. e .x findByTitle(String title) then Spring is automatically searching the Title Colum for the given String. If i have an int column named numberOfCopies and i want only to find the datasets with >0 greater then null how would define such a method ?
to filter out those books with the numberOfCopies equals 0 = zero
#Entity
public class Book {
#Id
private int id;
private String title;
private int numberOfCopies;
}
can i use the Repomethod
public List findBooksByNumberOfCopies.greater then 0 ?To Use this Spring Feature without some if or for loops
First, you should use Integer, since it is better, in my opinion, to use wrapper classes than to primitives, and enforce not null requirement through annotations, e.g. #Column(nullable = false)
#Entity
public class Book {
#Id
private Integer id;
private String title;
private Integer numberOfCopies;
}
Then you can add the following two methods in your BookRepository;
List<Book> findByNumberOfCopiesGreaterThan(Integer numberOfCopies);
default List<Book> findAllAvailableBooks() {
return findByNumberOfCopiesGreaterThan(0);
}
and use the default findAllAvailableBooks method, with hardcoded 0 value which is your requirement.
you can easily use
List<Book> findByNumberOfCopiesGreaterThanEqual(int numberOfCopies);
Pretty sure this would work:
public interface BookRepo extends JpaRepository<Book, Integer> {
#Query("SELECT b FROM Book b WHERE b.numberOfCopies >= 0")
public Optional<List<Book>> getTheBooksWithMultCopies();
}
// back in your component class:
{
...
Optional<List<Book>> optionalBookList = myBookRepo.getTheBooksWithMultCopies();
if (optionalBookList.isPresent()){
List<Book> bookList = optionalBookList.get();
}
}
Note that the language within the query is called HQL, which is what is used by Hibernate internally (which is used by JPA internally). It's really not very intimidating - just, know that you the objects in your POJO, which map to your database table, rather than your database table directly.
Also, I'd recommend using Integer over int in entity classes, at least if your value is nullable. Otherwise, numberOfCopies will always default to 0, which may not be desirable and may cause exceptions that are difficult to decipher.
GreaterThanEqual takes an Integer not int
List<Book> findByNumberOfCopiesGreaterThanEqual(Integer numberOfCopies);

Sorting on #Transient column in Spring Data Rest via PagingAndSortingRepository

Our application uses PagingAndSortingRepository to serve our REST API. This works great, but we ran into a specific edge case that we can't seem to solve:
We have a alphanumeric field that has to be sortable (e.g. SOMETHING-123). One possible solution was to use something like a regex inside the database query's order by. This was ruled out, as we wanted to stay database independant. Thus we split up the column into two columns.
So before we had an Entity with 1 String field:
#Entity
public class TestingEntity {
#Id
#GeneratedValue
private long id;
private String alphanumeric
}
And now we have an Entity with 2 additional fields and the old field made #Transient which is filled at #PostLoad:
#Entity
public class Testing {
#Id
#GeneratedValue
private long id;
#Transient
public String alphanumeric;
#PostLoad
public void postLoad(){
this.alphanumeric = this.alphabetic + "-" + this.numeric;
}
public void setAlphanumeric(String alphanumeric) {
int index = alphanumeric.indexOf("-");
this.alphabetic = alphanumeric.substring(0, index);
this.numeric = Long.parseLong(alphanumeric.substring(index + 1));
}
#JsonIgnore
private String alphabetic;
#JsonIgnore
private Long numeric;
}
This is working great and the additional fields do not get exposed. However the sorting on the field "alphanumeric" does obviously not work anymore. The simplest solution would be to make this request:
localhost:8080/api/testingEntity?sort=alphanumeric,asc
and internally rewrite it to the working request:
localhost:8080/api/testingEntity?sort=alphabetic,asc&sort=numeric,asc
What is the best way to tackle this issue?

How to query a database having a composite key, which accepts queries where parts of the composite key are null in Spring JPA?

I have a webapp that uses a database with a composite key. I need to create an API which will accept the parameters of the composite key wherein each paramenter can be null.
(for eg. If all the parameters of the composite key are set as null and query is done, then it should just return all rows in db)
I have used QueryByExampleExeccutor but it keeps throwing a null pointer exception.
Below is the model indicative of 1 row of the DB
public class Row
{
#EmbeddedId
private RowKey rowkey;
#Column
private String rowAttribute;
//getters and setters for rowkey and rowAttribute
}
Below is the model indicative of RowKey
public class RowKey
{
#Column(name = "rowBlock")
private String rowBlock;
#Column(name = "rowSection")
private String rowSection;
//getters and setters for above fields
}
I'm using QueryByExampleExecutor by extending my Repository(or DAO) with it as
public interface RowRepo extends CrudRepository<Row, RowKey>, QueryByExampleExecutor<Row> {
}
In my service layer I use the following statement to call repo methods to return a list of matched rows from DB
public List<Row> getRowRangeQuery(rowBlock,rowSection,rowAttribute)
{
Row row = new Row(new RowKey(rowBlock,rowSection),rowAttribute);
List<Row> Result = (List<Row>)getRowRepository().findAll(Example.of(row));
return Result;
}
I get an InvocationTargetException which when unrolled reveals a null pointer Exception at rowSection.

I need help for persisting into oracle database

There is a problem about generating id while persisting into database.
I added the following code to my jpa entity file, however I'm getting 0 for personid.
#Id
#Column(unique=true, nullable=false, precision=10, name="PERSONID")
#SequenceGenerator(name="appUsersSeq", sequenceName="SEQ_PERSON", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "appUsersSeq")
private long personid;
EjbService:
#Stateless
public class EjbService implements EjbServiceRemote {
#PersistenceContext(name = "Project1245")
private EntityManager em;
#Override
public void addTperson(Tperson tp) {
em.persist(tp);
}
}
0 is default value for long type. The id will be set after invoking select query for the related sequence, which commonly is executed when you persist the entity. Are you persisting the entity? In case yes, post the database sequence definition to check it.

Variable 'this.userInfo' is unbound and cannot be determined

I am developing a maven JDO project, but I am getting this error when I am trying to make relation between two tables (user_login, user_role)
User_Login: user_id(primary key), user_name, user_password,user_role_id
User_Role: id(primary key), role
user_role_id is same as id of user_role table
User.java:
#PersistenceCapable(table = "user_login")
public class User {
#PrimaryKey
#Column(name="user_id")
private Integer userId=0;
#Column(name="user_profile_name")
private String userProfileName=null;
#Column(name="user_email")
private String userEmail=null;
#Column(name="user_contact")
private String userContact=null;
#Column(name="user_name")
private String userName=null;
#Column(name="user_password")
private String userPassword=null;
#ManyToOne
#Column(name="user_role_id")
private Integer userRoleId=0;
Role.java:
#PersistenceCapable(table = "user_role")
public class Role {
#PrimaryKey
#Column(name="id")
private Integer id=0;
#Column(name="role")
private String role=null;
#OneToMany
private User userInfo=null;
DAOImpol:
public List<Role> getUser(String username, String userpassword) {
PersistenceManager pm = this.pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
JDOPersistenceManager jdopm = (JDOPersistenceManager)pm;
try {
// Start the transaction
tx.begin();
TypesafeQuery<User> tq = jdopm.newTypesafeQuery(User.class);
//QUser user = QUser.candidate();
QRole role = QRole.candidate();
QUser userInfo=role.userInfo;
List<Role> result = tq.filter(userInfo.userName.eq(username).and(userInfo.userPassword.eq(userpassword))).executeList();
//result = tq.executeResultList(true, user.userId);
if(result.size()>0){
log.info(">>>>>00000000"+" "+result.get(0).getUser().getUserEmail());
log.info(">>>>>11111111"+" "+result.get(0).getRoleId()+" "+result.get(0).getRole());
}else{
log.info("<<<<<<<=====000000");
}
// Commit the transaction, flushing the object to the datastore
tx.commit();
return result;
}
finally {
if (tx.isActive())
{
// Error occurred so rollback the transaction
tx.rollback();
}
pm.close();
}
I am getting this error:
javax.jdo.JDOUserException: Variable 'this.userInfo' is unbound and
cannot be determined (is it a misspelled field name? or is not intended
to be a variable?)
NestedThrowables:
org.datanucleus.exceptions.NucleusUserException: Variable
'this.userInfo' is unbound and cannot be determined (is it a
misspelled
field name? or is not intended to be a variable?)
I found that you'll get this error from JDO if you're using progaurd and progaurd renames your private fields. Adding a -keep to the progaurd config to keep the package with your Persistence Capable classes will fix it.
For example, if you keep all of your Persistence Capable classes in com.example.server.orm package you'd add this to progaurd.conf
-keep class com.example.server.orm.** {*;}

Resources