Data On Init With Spring Command Line Runner - java

Here , I want to hardcode some things into db at the action of server starts with Spring commandline runner
Problem :- In this I have checked that if 1L isn't present then do entry for that with Id 1L but still it stores to incremental Id when 1L or 2L or 3L is not present there.
My Entity In which I'm doing entry :-
Product Type
#Data
#Entity
#Table(name = "tbl_product_type_chemical")
public class ProductType implements IBaseData<Long> {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
private String name;
#Column(columnDefinition = "text")
private String description;
}
This is method of Runner :-
private void loadProductTypeNew() throws Exception {
String SUBMODULE = " [Init Data] " + " [loadProductTypeNew()] ";
try {
ProductType fp = productTypeRepository.getOne(1L);
if (null == fp) {
fp = new ProductType();
fp.setId(1L);
fp.setName("FINISH PRODUCT");
productTypeRepository.save(fp);
}
ProductType rm = productTypeRepository.getOne(2L);
if (null != rm) {
rm = new ProductType();
rm.setId(2L);
rm.setName("RAW MATERIAL");
productTypeRepository.save(rm);
}
ProductType sm = productTypeRepository.getOne(3L);
if (null != sm) {
sm = new ProductType();
sm.setId(3L);
sm.setName("SUPPORTING MATERIAL");
productTypeRepository.save(sm);
}
} catch (Exception ex) {
ApplicationLogger.logger.error(SUBMODULE + ex.getMessage(), ex);
throw ex;
}
}
#Override
public void run(String... args) throws Exception {
loadProductTypeNew();`
}
Output :-
9 SUPPORTING MATERIAL
8 RAW MATERIAL
7 FINISH PRODUCT
10 FINISH PRODUCT
11 RAW MATERIAL
12 SUPPORTING MATERIAL
13 RAW MATERIAL
14 SUPPORTING MATERIAL
And I am calling it in run method.If anyone can solve thanks in advance

Nothing in your application should depend on specific values of the id field since it is generated by the database.
I suggest therefore to identify a business key (in your case the name), ensure it is unique with a primary key and use that to determine if a a row needs to be inserted in the database.
Your code would look similar to this
if (productTypeRepository.existsByName(""FINISH PRODUCT")) {
ProductType fp = new ProductType();
fp.setName("FINISH PRODUCT");
productTypeRepository.save(fp);
}

Related

Failed to obtain a generated Id with my programme. GWT

I am working on a client-server project in GWT and after the user fills a form, I would like to update my database and eventually create new datas.
For that, I create a "sensor" and the system link with which it is associated. But, the "sensor" is associated with a sensor Family too and I must create the link in my table sensorFamilySensorFamilyLink but I do not managed.
Indeed, here is my code for the creation of the sensor and the link:
#Override
public void updateSensor(SystemDTO system, SensorDTO sensorDTO, String sensorFamName) {
Sensor sensor = null;
SensorFamily sensorFam = null;
if(sensorDTO.getId() <0) {
sensor = new Sensor();
sensor.fillFromDTO(sensorDTO);
sensor.create();
}
else {
sensor = Sensor.queryById(sensorDTO.getId());
sensor.fillFromDTO(sensorDTO);
sensor.update();
}
sensorFam = SensorFamily.queryByName(sensorFamName);
// Creation of the link between sensor and sensorFamily.
SensorFamilySensorLink linksensor = null;
if (!linkFam.isEmpty()) {
linksensor = linkFam.get(0); // We take the first one because there is only one.
}
if(linksensor == null) { // creation of a sensor
//List<SensorFamilySensorLink> liste = SensorFamilySensorLink.query();
linksensor = new SensorFamilySensorLink();
linksensor.setSensorId(sensor.getId());
linksensor.setSensorFamilyId(sensorFam.getId());
linksensor.create();
}
else {
linksensor.setSensorFamilyId(sensorFam.getId());
linksensor.update();
}
Whereas the update works, the creation of the link does not work. Here is my code for the method "create":
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class SensorFamilySensorLink {
/** The id. */
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private long id;
/** The associated sensor id. */
#Persistent
private long sensorId;
/** The associated sensorFamily id. */
#Persistent
private long sensorFamilyId;
/**
* Create.
*/
public void create() {
PersistenceManager pm = PMF.getManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(this);
tx.commit();
} catch(Exception e) {
} finally {
if (tx.isActive()) {
tx.rollback(); // Error occurred so rollback the PM transaction
}
}
pm.close();
}
After debuggind, the problem comes from the generation if the IDs to use the SQL method INSERT in the table. THe generated id does not work when I am doing:
pm.makePersistent(this);
Is someone can say to me what I have forgotten because I have been searching for two days and I do not how to solve the problem? Besides, I use the same code for creating the sensor link with the system and the family link and it is working for the first one.
If you need more information, do not hesitate to ask me. Thanks in advance.

JPA conditional insertion

I have a Java Spring based web application and I want to insert a record to a table only if the table does not contain any rows that are "similar" (according to some specific, irrelevant criteria) to the new row.
Because this is a multi-threaded environment, I cannot use a SELECT+INSERT two-step combination as it would expose me to a race condition.
The same question was first asked and answered here and here several years ago. Unfortunately, the questions have got only a little attention and the provided answer is not sufficient to my needs.
Here's the code I currently have and it's not working:
#Component("userActionsManager")
#Transactional
public class UserActionsManager implements UserActionsManagerInterface {
#PersistenceContext(unitName = "itsadDB")
private EntityManager manager;
#Resource(name = "databaseManager")
private DB db;
...
#SuppressWarnings("unchecked")
#Override
#PreAuthorize("hasRole('ROLE_USER') && #username == authentication.name")
public String giveAnswer(String username, String courseCode, String missionName, String taskCode, String answer) {
...
List<Submission> submissions = getAllCorrectSubmissions(newSubmission);
List<Result> results = getAllCorrectResults(result);
if (submissions.size() > 0
|| results.size() > 0) throw new SessionAuthenticationException("foo");
manager.persist(newSubmission);
manager.persist(result);
submissions = getAllCorrectSubmissions(newSubmission);
results = getAllCorrectResults(result);
for (Submission s : submissions) manager.lock(s, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
for (Result r : results ) manager.lock(r, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
manager.flush();
...
}
#SuppressWarnings("unchecked")
private List<Submission> getAllCorrectSubmissions(Submission newSubmission) {
Query q = manager.createQuery("SELECT s FROM Submission AS s WHERE s.missionTask = ?1 AND s.course = ?2 AND s.user = ?3 AND s.correct = true");
q.setParameter(1, newSubmission.getMissionTask());
q.setParameter(2, newSubmission.getCourse());
q.setParameter(3, newSubmission.getUser());
return (List<Submission>) q.getResultList();
}
#SuppressWarnings("unchecked")
private List<Result> getAllCorrectResults(Result result) {
Query q = manager.createQuery("SELECT r FROM Result AS r WHERE r.missionTask = ?1 AND r.course = ?2 AND r.user = ?3");
q.setParameter(1, result.getMissionTask());
q.setParameter(2, result.getCourse());
q.setParameter(3, result.getUser());
return (List<Result>) q.getResultList();
}
...
}
According to the answer provided here I am supposed to somehow use OPTIMISTIC_FORCE_INCREMENT but it's not working. I suspect that the provided answer is erroneous so I need a better one.
edit:
Added more context related code. Right now this code still has a race condition. When I make 10 simultaneous HTTP POST requests approximately 5 rows will get erroneously inserted. Other 5 requests are rejected with HTTP error code 409 (conflict). The correct code would guarantee that only 1 row would get inserted to the database no matter how many concurrent requests I make. Making the method synchronous is not a solution since the race condition still manifests for some unknown reason (I tested it).
Unfortunately after several days of research I was unable to find a short and simple solution to my problem. Since my time budget is not unlimited I had to come up with a workaround. Call it a kludge if you may.
Since the whole HTTP request is a transaction, it will be rolled back at the sight of any conflicts. I am using this for my advantage by locking a special entity within the context of the whole HTTP request. Should multiple HTTP requests be received at the same time, all but one will result in some PersistenceException.
In the beginning of the transaction I am checking whether no other correct answers have been submitted yet. During that check the lock is already effective so no race condition could happen. The lock is effective until the answer is submitted. This basically simulates a critical section as a SELECT+INSERT two step query on the application level (in pure MySQL I would have used the INSERT IF NOT EXISTS construct).
This approach has some drawbacks. Whenever two students submit an answer at the same time, one of them will be thrown an exception. This is sort of bad for performance and bandwidth because the student who received HTTP STATUS 409 has to resubmit their answer.
To compensate the latter, I am automatically retrying to submit the answer on the server side a couple of times between randomly chosen time intervals. See the according HTTP request controller code is below:
#Controller
#RequestMapping("/users")
public class UserActionsController {
#Autowired
private SessionRegistry sessionRegistry;
#Autowired
#Qualifier("authenticationManager")
private AuthenticationManager authenticationManager;
#Resource(name = "userActionsManager")
private UserActionsManagerInterface userManager;
#Resource(name = "databaseManager")
private DB db;
.
.
.
#RequestMapping(value = "/{username}/{courseCode}/missions/{missionName}/tasks/{taskCode}/submitAnswer", method = RequestMethod.POST)
public #ResponseBody
Map<String, Object> giveAnswer(#PathVariable String username,
#PathVariable String courseCode, #PathVariable String missionName,
#PathVariable String taskCode, #RequestParam("answer") String answer, HttpServletRequest request) {
init(request);
db.log("Submitting an answer to task `"+taskCode+"` of mission `"+missionName+
"` in course `"+courseCode+"` as student `"+username+"`.");
String str = null;
boolean conflict = true;
for (int i=0; i<10; i++) {
Random rand = new Random();
int ms = rand.nextInt(1000);
try {
str = userManager.giveAnswer(username, courseCode, missionName, taskCode, answer);
conflict = false;
break;
}
catch (EntityExistsException e) {throw new EntityExistsException();}
catch (PersistenceException e) {}
catch (UnexpectedRollbackException e) {}
try {
Thread.sleep(ms);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
if (conflict) str = userManager.giveAnswer(username, courseCode, missionName, taskCode, answer);
if (str == null) db.log("Answer accepted: `"+answer+"`.");
else db.log("Answer rejected: `"+answer+"`.");
Map<String, Object> hm = new HashMap<String, Object>();
hm.put("success", str == null);
hm.put("message", str);
return hm;
}
}
If for some reason the controller is unable to commit the transaction 10 times in a row then it will try one more time but will not attempt to catch the possible exceptions. When an exception is thrown on the 11th try then it will be processed by the global exception controller and the client will receive HTTP STATUS 409. The global exception controller is defined below.
#ControllerAdvice
public class GlobalExceptionController {
#Resource(name = "staticDatabaseManager")
private StaticDB db;
#ExceptionHandler(SessionAuthenticationException.class)
#ResponseStatus(value=HttpStatus.FORBIDDEN, reason="session has expired") //403
public ModelAndView expiredException(HttpServletRequest request, Exception e) {
ModelAndView mav = new ModelAndView("exception");
mav.addObject("name", e.getClass().getSimpleName());
mav.addObject("message", e.getMessage());
return mav;
}
#ExceptionHandler({UnexpectedRollbackException.class,
EntityExistsException.class,
OptimisticLockException.class,
PersistenceException.class})
#ResponseStatus(value=HttpStatus.CONFLICT, reason="conflicting requests") //409
public ModelAndView conflictException(HttpServletRequest request, Exception e) {
ModelAndView mav = new ModelAndView("exception");
mav.addObject("name", e.getClass().getSimpleName());
mav.addObject("message", e.getMessage());
synchronized (db) {
db.setUserInfo(request);
db.log("Conflicting "+request.getMethod()+" request to "+request.getRequestURI()+" ("+e.getClass().getSimpleName()+").", Log.LVL_SECURITY);
}
return mav;
}
//ResponseEntity<String> customHandler(Exception ex) {
// return new ResponseEntity<String>("Conflicting requests, try again.", HttpStatus.CONFLICT);
//}
}
Finally, the giveAnswer method itself utilizes a special entity with a primary key lock_addCorrectAnswer. I lock that special entity with the OPTIMISTIC_FORCE_INCREMENT flag which makes sure that no two transactions can have overlapping execution times for the giveAnswer method. The respective code can be seen below:
#Component("userActionsManager")
#Transactional
public class UserActionsManager implements UserActionsManagerInterface {
#PersistenceContext(unitName = "itsadDB")
private EntityManager manager;
#Resource(name = "databaseManager")
private DB db;
.
.
.
#SuppressWarnings("unchecked")
#Override
#PreAuthorize("hasRole('ROLE_USER') && #username == authentication.name")
public String giveAnswer(String username, String courseCode, String missionName, String taskCode, String answer) {
.
.
.
if (!userCanGiveAnswer(user, course, missionTask)) {
error = "It is forbidden to submit an answer to this task.";
db.log(error, Log.LVL_MAJOR);
return error;
}
.
.
.
if (correctAnswer) {
.
.
.
addCorrectAnswer(newSubmission, result);
return null;
}
newSubmission = new Submission(user, course, missionTask, answer, false);
manager.persist(newSubmission);
return error;
}
private void addCorrectAnswer(Submission submission, Result result) {
String var = "lock_addCorrectAnswer";
Global global = manager.find(Global.class, var);
if (global == null) {
global = new Global(var, 0);
manager.persist(global);
manager.flush();
}
manager.lock(global, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
manager.persist(submission);
manager.persist(result);
manager.flush();
long submissions = getCorrectSubmissionCount(submission);
long results = getResultCount(result);
if (submissions > 1 || results > 1) throw new EntityExistsException();
}
private long getCorrectSubmissionCount(Submission newSubmission) {
Query q = manager.createQuery("SELECT count(s) FROM Submission AS s WHERE s.missionTask = ?1 AND s.course = ?2 AND s.user = ?3 AND s.correct = true");
q.setParameter(1, newSubmission.getMissionTask());
q.setParameter(2, newSubmission.getCourse());
q.setParameter(3, newSubmission.getUser());
return (Long) q.getSingleResult();
}
private long getResultCount(Result result) {
Query q = manager.createQuery("SELECT count(r) FROM Result AS r WHERE r.missionTask = ?1 AND r.course = ?2 AND r.user = ?3");
q.setParameter(1, result.getMissionTask());
q.setParameter(2, result.getCourse());
q.setParameter(3, result.getUser());
return (Long) q.getSingleResult();
}
}
It is important to note that the entity Global has to have a version annotation in its class for the OPTIMISTIC_FORCE_INCREMENT to work (see code below).
#Entity
#Table(name = "GLOBALS")
public class Global implements Serializable {
.
.
.
#Id
#Column(name = "NAME", length = 32)
private String key;
#Column(name = "INTVAL")
private int intVal;
#Column(name = "STRVAL", length = 4096)
private String strVal;
#Version
private Long version;
.
.
.
}
Such an approach can be optimized even further. Instead of using the same lock name lock_addCorrectAnswer for all giveAnswer calls, I could generate the lock name deterministically from the name of the submitting user. For example, if the student's username is Hyena then the primary key for the lock entity would be lock_Hyena_addCorrectAnswer. That way multiple students could submit answers at the same time without receiving any conflicts. However, if a malicious user spams the HTTP POST method for submitAnswer 10x in parallel they will be prevented by the this locking mechanism.

GAE + JDO : Deleting Child object cause issue in google app engine java

I am using JDO in GAE. I have two JDO classes having one to many relationship. parent class is
#PersistenceCapable(detachable="true")
#FetchGroup(name="childerns", members={#Persistent(name="aliasName")})
public class IdentityProvider {
#PrimaryKey
#Persistent
private String url;
#Persistent
private String domainName;
#Persistent
#Element(dependent = "true")
private ArrayList<AliasDomain> aliasName = new ArrayList<AliasDomain>();
}
The child classes is
#PersistenceCapable(detachable = "true")
public class AliasDomain {
#Persistent
private String url;
#Persistent
private String aliasName;
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
}
I am just performing CURD operations on both entity. First i create the parent instance and then i create the child instance as
public void addAliasDomain(AliasDomain domain) {
String url = domain.getUrl();
PersistenceManager pm = PMFSingleton.get().getPersistenceManager();
IdentityProvider idp = null;
Transaction txn = null;
try {
txn = pm.currentTransaction();
txn.begin();
pm.getFetchPlan().addGroup("childerns");
idp = pm.getObjectById(IdentityProvider.class, url);
idp = pm.detachCopy(idp);
idp.getAliasName().add(domain);
pm.makePersistent(idp);
txn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
if ( txn.isActive() ) {
txn.rollback();
}
pm.close();
}
}
My issue is created when i delete any child instance. As you see from the above function i link the child to the parents( means add child object into arrayList). So when child is deleted its reference in the parents is not deleted so at the detached time of parents object i got exception which is as
Object of type "user.oauth.jdo.model.IdentityProvider" and identity "yahoo.com" was not detached correctly. Please consult the log for any possible information.
org.datanucleus.exceptions.NucleusUserException: Object of type "user.oauth.jdo.model.IdentityProvider" and identity "yahoo.com" was not detached correctly. Please consult the log for any possible information.
at org.datanucleus.state.JDOStateManager.detachCopy(JDOStateManager.java:2942)
at org.datanucleus.ObjectManagerImpl.detachObjectCopy(ObjectManagerImpl.java:2591)
at org.datanucleus.api.jdo.JDOPersistenceManager.jdoDetachCopy(JDOPersistenceManager.java:1145 )
at org.datanucleus.api.jdo.JDOPersistenceManager.detachCopy(JDOPersistenceManager.java:1174)
at user.oauth.data.broker.IDPJDOBroker.retrieveDomainList(IDPJDOBroker.java:49)
The code of function retreiveDomainList in IDPJDOBroker is
public List retrieveDomainList() {
PersistenceManager pm = PMFSingleton.get().getPersistenceManager();
Query query = pm.newQuery(IdentityProvider.class);
List<IdentityProvider> list = null;
List<IdentityProvider> detachedList = null;
IdentityProvider idp = null;
try {
pm.getFetchPlan().addGroup("childerns");
list = (List<IdentityProvider>) query.execute();
detachedList = new ArrayList<IdentityProvider>();
for(IdentityProvider obj : list){
idp = pm.detachCopy(obj);
OAuthJDOBroker broker = new OAuthJDOBroker();
int actUsers = 0;
if ( idp.getHistory() != null && idp.getHistory().size() > 0) {
actUsers = broker.calculateActiveUser(idp.getUserActiveDuration(),idp.getDomainName());
}
idp.setActiveUsers(actUsers);
detachedList.add(idp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
query.closeAll();
pm.close();
}
return detachedList;
}
Please tell me what to do? Is it not possible in JDO to delete the child? if it is possible then how to do it properly.
I have just seen this, but in case anyone arrives here, to delete a child object in a one to many relationship you must delete the reference from the parent, the child object will be deleted "transparently"

Oracle Berkeley DB Java Edition - Secondary key unicity

I have a simple entity class and it is supposed to include unique names on it.
#Entity
class Package {
#PrimaryKey(sequence = "ID")
public Long id;
#SecondaryKey(relate = Relationship.ONE_TO_ONE)
public String name;
private Package() {}
public Package(String name) { this.name = name; }
#Override
public String toString() { return id + " : " + name; }
}
I want to use deferred writing option because of extensive modification. Here is the test i tried and its output.
final String dbfilename = "test01";
new File(dbfilename).mkdirs();
EnvironmentConfig config = new EnvironmentConfig().setAllowCreate(true);
Environment environment = new Environment(new File(dbfilename), config);
StoreConfig storeConfig = new StoreConfig().setAllowCreate(true).setDeferredWrite(true);
EntityStore store = new EntityStore(environment, "", storeConfig);
PrimaryIndex<Long, Package> primaryIndex = store.getPrimaryIndex(Long.class, Package.class);
try {
primaryIndex.put(new Package("package01")); // will be put.
primaryIndex.put(new Package("package01")); // throws exception.
} catch (UniqueConstraintException ex) {
System.out.println(ex.getMessage());
}
store.sync(); // flush them all
// expecting to find one element
SortedMap<Long,Package> sortedMap = primaryIndex.sortedMap();
for (Package entity : sortedMap.values()) {
System.out.println(entity);
}
Output
(JE 5.0.73) Unique secondary key is already present
1 : package01
2 : package01
So my question is that even if it throws exception while putting second package, why does it lists two packages. Any way to avoid this without using transactions?
Thanks.

java google app engine get entity by key

I have a problem using GAEJ and JDO for storing the data.
This is what I'm working with:
class Usuari.java:
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String email;
#Persistent
private String rol="";
class DBUtils.java:
I've tried with two ways of doing the delete operation:
// This method removes a record from the database using its unique Key
public static boolean eliminar(Key k) throws Exception {
PersistenceManager pm = PMF.get().getPersistenceManager();
String kind;
Long id;
kind = k.getKind();
id = k.getId();
try {
if (k.getKind().equals("Usuari")) {
Usuari u = (Usuari)pm.getObjectById(k);
pm.deletePersistent(u);
_log.log(Level.INFO, "Deleted an entity->kind: " + kind + " id: " + id);
}
return true;
} catch (Exception e) {
_log.log(Level.SEVERE, "Unable to delete an entity->kind: " + kind + " id: " + id);
System.err.println(e.getMessage());
throw e;
}
finally {
pm.close();
}
}
// This method removes a record from the database using its unique Key - too
public static void eliminar2(Key k) throws Exception {
PersistenceManager pm = PMF.get().getPersistenceManager();
javax.jdo.Transaction tx = pm.currentTransaction();
try
{
tx.begin();
if (k.getKind().equals("Usuari")) {
Usuari u = (Usuari) pm.getObjectById(k);
pm.deletePersistent(u);
}
tx.commit();
}
catch (Exception e)
{
if (tx.isActive())
{
tx.rollback();
}
throw e;
}
}
I'm able to create new instances of some class "Usuari" but I can't delete them.
Everytime I call "eliminar" or "eliminar2" methods I get a "No such object" as result of trying to fetch it. I've checked manually and I see the object exists in my admin panel, with its ID and KIND, so I don't know what am I doing wrong.
Any help would be much appreciated.
PM.getObjectById does not take in a Key object, as per the JDO spec. It takes in an identity object, the same type as you would get from pm.getObjectId(obj); suggest you glance through the JDO spec. No doubt if you inspected what is returned from this method you would see that it can't find an object with that 'identity' because a Key is not an identity. You can also do
pm.getObjectById(Usuari.class, key);
which is shown very clearly in GAE documentation.
Still don't get why users are putting #Persistent on every field virtually every type is default persistent; only leads to making code more unreadable.

Categories