DB management using Entity Objects - java

I have a bean in my Fusion Web Application where I'm supposed to insert new data into a table of my database through java code (after appropriate validation).
The question is how should I do the insertion?
Should I use Entity Objects?
How?
P.S.: This is not the way it should work http://jneelmani.blogspot.com/2009/11/adf-insert-using-storeprocedure.html

I created Entity Object and View Object by the database table "Employees" and then created application module where included this view object (also were generated java classes for entity object, view object and appModule. EmployeeInfo is just POJO). Inside the application module I created methods:
public EmployeeViewRowImpl saveEmployee(EmployeeInfo EmployeeInfo) {
// Получаем ViewObject
EmployeeViewImpl employeeView = getEmployeeView1();
// Готовим новую строку.
EmployeeViewRowImpl employee = createEmployeeViewRowImpl(employeeView, employeeInfo);
// Производим операцию вставки.
employeeView.insertRow(employee);
// Коммитим
try {
getDBTransaction().commit();
return employee;
} catch (JboException e) {
getDBTransaction().rollback();
return null;
}
}
private EmployeeViewRowImpl createEmployeeViewRowImpl(EmployeeViewImpl employeeView, EmployeeInfo employeeInfo) {
EmployeeViewRowImpl employee = (EmployeeViewRowImpl)EmployeeView.createRow();
employee.setName(employeeInfo.getName());
return employee;
}
And to use this one should just call:
public static AppModuleImpl getApp() {
return (AppModuleImpl)Configuration.
createRootApplicationModule(
"com.test.service.AppModule", // where your module is stored
"AppModuleShared"); // chosen configuration
}
and then
...
RegistrationAppModuleImpl app = getApp();
app.saveUser(userInfo)
...

May be i'm not to clear on the dynamics of what you are trying to do, but with Oracle ADF, CRUD operations (such as Insert), are easily handled by exposing them from Data Controls. To be more specific, once you have an EO, you should create a View Object and an Application Module. After that, inside the AppMod -> Data Model , add the created VO. This way it will be exposed in the Data Controls panel, and you can expand the 'Operations' folder, and drag'n'drop the CreateInsert operation possibly within a form, or an updatable table.
Please refer to this link: CreateInsert Operation - ADF.
If for some other reason you want to handle this process in a programmatic approach, i might think about two possible ways:
1. Get into your managed bean code an instance of the above mentioned AppMod, and from that, a VO instance.
AppModule mod = AppModule)Configuration.createRootApplicationModule("packageName.AppModule", "AppModuleLocal");
ViewObject vo = mod.getViewObject1();After that, create a new row and commit the newly added values.
2. If you have already exposed a UI component (such a table), you can grab the Binding Context of the current page and from the table's iterator, create a new row.
DCBindingContainer DCB = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding iterator = bc.findIteratorBinding("ViewObject1Iterator");
Row r = iterator.getCurrentRow();
r.setAttribute("attibName", attribValue);

You can do the insertion using entity object as below:
/* Create a new Customer and Return the new id */
public long createCustomer(String name, String city, Integer countryId) {
EntityDefImpl customerDef = CustomerImpl.getDefinitionObject();
CustomerImpl newCustomer =
(CustomerImpl)customerDef.createInstance2(getDBTransaction(),null);
newCustomer.setName(name);
newCustomer.setName(name);
newCustomer.setCountryId(countryId);
try {
getDBTransaction().commit();
}
catch (JboException ex) {
getDBTransaction().rollback();
throw ex;
}
DBSequence newIdAssigned = newCustomer.getId();
}

Related

In Hibernate why does saveOrUpdate give an exception when object already exists in database

Previously, when I was adding a entity to database with Hibernate I used to check that it hadn't already been added. But in an effort to improve performance I forgot this check and just tried to add without checking, as I was using saveOrUpdate() it was my understanding that if Hibernate found it was already added it would just update with and changes made by my save.
But instead it fails with
18/08/2018 21.58.34:BST:Errors:addError:SEVERE: Adding Error:Database Error:Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.jthink.songlayer.MusicBrainzReleaseWrapper#95f6f584-407f-4b26-9572-bb8c6e9c580a]
java.lang.Exception
at com.jthink.songkong.analyse.general.Errors.addError(Errors.java:28)
at com.jthink.songkong.exception.ExceptionHandling.handleHibernateException(ExceptionHandling.java:209)
at com.jthink.songkong.db.ReleaseCache.addToDatabase(ReleaseCache.java:394)
at com.jthink.songkong.db.ReleaseCache.add(ReleaseCache.java:65)
#Entity
public class MusicBrainzReleaseWrapper
{
#Id
private String guid;
#Version
private int version;
#org.hibernate.annotations.Index(name = "IDX__MUSICBRAINZ_RELEASE_WRAPPER_NAME")
#Column(length = 1000)
private String name;
#Lob
#Column(length = 512000)
private String xmldata;
public String getGuid()
{
return guid;
}
public void setGuid(String guid)
{
this.guid = guid;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getXmldata()
{
return xmldata;
}
public void setXmldata(String xmldata)
{
this.xmldata = xmldata;
}
}
private static boolean addToDatabase(Release release)
{
Session session = null;
try
{
session = HibernateUtil.beginTransaction();
//Marshall to String
StringWriter sw = new StringWriter();
Marshaller m = jc.createMarshaller();
m.marshal(release, sw);
sw.flush();
MusicBrainzReleaseWrapper wrapper = new MusicBrainzReleaseWrapper();
wrapper.setGuid(release.getId());
wrapper.setName(release.getTitle().toLowerCase(Locale.UK));
wrapper.setXmldata(sw.toString());
session.saveOrUpdate(wrapper);
session.getTransaction().commit();
MainWindow.logger.info("Added to db release:" + release.getId() + ":" + release.getTitle());
return true;
}
catch (ConstraintViolationException ce)
{
MainWindow.logger.warning("Release already exists in db:"+release.getId()+":"+release.getTitle());
return true;
}
catch(GenericJDBCException jde)
{
MainWindow.logger.log(Level.SEVERE, "Failed:" +jde.getMessage());
ExceptionHandling.handleDatabaseException(jde);
}
catch(HibernateException he)
{
MainWindow.logger.log(Level.SEVERE, "Failed:" +he.getMessage());
ExceptionHandling.handleHibernateException(he);
}
catch(Exception e)
{
MainWindow.logger.log(Level.WARNING,"Failed AddReleaseToDatabase:"+release.getId()+ ':' +e.getMessage(),e);
throw new RuntimeException(e);
}
finally
{
HibernateUtil.closeSession(session);
}
return false;
}
Used to check first before call to addToDatabase
if(ReleaseCache.get(release.getId())==null)
{
addToDatabase(release)
}
Hiberante object has 3 states for an Entity. They are:
- Transient Or New
- Detached (Objects are fetched from DB and hibernate session is closed)
- Persistent (Object are fetched from DB and hibernate session is open)
In saveOrUpdate method, it either save the transient object or update the detached/ persistent object.
In your code, you are trying to create Transient/New object and setting the old id in it. That's the reason you are getting above error. The correct way to fetch the object first using id and then update it.
The problem you are hitting is directly related to the Optimistic locking you have enabled through the #Version annotation on the MusicBrainzReleaseWrapper. saveOrUpdate really can either add or update an entity but this is only if the entity version is the same as the one of the detached object you are trying to add or merge.
In your particular example your detached object has a version previous to the last version in the database therefore the operation can not be executed on a stale data.
UPDATE:
MusicBrainzReleaseWrapper wrapper = session.get(release.getId()):
//the wrapper is managed object
if (wrapper == null) {
//initilize wrapper with the values from release
.......
session.save(wrapper)
}
else {
// do not set ID here. ID is aready present!!!
// never manuay set the version field here
wrapper.setName(release.getTitle().toLowerCase(Locale.UK));
wrapper.setXmldata(sw.toString());
session.saveOrUpdate(wrapper);
//In case you don't need update logic at all
// remove the #Version field from the entity
// and do othing in the else clause , or throw exception
// or log error or anything you see fit
}
No. saveOrUpdate method is used either to persist or merge an entity with the current session. It doesn't do what you expect. Either save or update entity is application's specific logic. Hibernate doesn't do any application's specific logic.
Session.merge() can directly save a previously unknown instance, but note it won't necessarily avoid the extra select against the database.
#Pavan is right about the entity being transient or detached in Hibernate (or JPA) terminology. Both of these states mean that Hibernate has not yet got a reference to this instance of the entity in its session (in the StatefulPersistenceContext), but detached clearly means it is known to the database.
merge() instructs Hibernate to stop and check for a detached instance. The first check is for the #Id value in the session, but if it's not already there, it must hit the database.
saveOrUpdate() instructs Hibernate that the caller knows it is safe to only check the StatefulPersistenceContext for the #Id. If it's not there, the entity is assumed to be transient (i.e. new), and Hibernate will proceed to the insert operation.
saveOrUpdate() is good for instances (with or without an #Id value) that are known to the session already.
In your case clearly Hibernate is unaware of the detached instance, so you would need to use merge(). But that also means Hibernate has to check the database for the instance it hasn't seen before - if the entity has an #Id value.
To come back to the original intent in your question, update without select is harder ...
For an update, Hibernate likes to know the prior state of the entity. This makes sense if it's using dynamic updates (so not updating all columns), but otherwise you would think it could go straight for the update. The only option I know of for this is a direct update query (via HQL or JPQL), but this is hardly convenient if you have an entity instance. Maybe someone else knows how to do this.

How to retrieve a list from DynamoDB?

Im having a specific error when running a scan expression on a table within my DynamoDB Database. There is one item in my Projects table as of now. This item contains a project description and a list of Strings of team mates. When retrieving the information for the project description my code prints out the correct name of the project. However when trying to retrieve the list of team mates from this same item it says that the list is a null object reference. I can not seem to understand why the list being returned is null. Assume all permissions have been set properly within the IAM console, and the database.
Below is the code for my thread which scans the table.
public void run() {
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
//returns a list of items from the table. each item is of Project type
List<Project> scanResult=mapper.scan(Project.class,scanExpression);
//for each project within scanResult do the following
for (Project project: scanResult){
//retrieve the name of the team (this portion of the code logs the project name properly)
String team=project.getProjectname();
Log.v("team",team.toString());
//The list being returned from this one line below is null??
List<String> teammates=project.getTeammates();
Log.v("Teammate", teammates.get(0));
}
}
};
Thread mythread = new Thread(runnable);
mythread.start();
Below is the code for the Projects Class which serves as a template when scanning the table. This is most likely the area of the issue because the project description string is being returned properly, however the List of teamMates isnt. Perhaps I am not supposed to be using a List, or the List is not defined properly, or the use of Java annotations is not done correctly on this table !!!! However I can not find the issue
package com.example.varun.finalproject;
import com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.*;
import java.util.List;
/**
* Created by Varun on 4/10/17.
*/
#DynamoDBTable(tableName = "SBUProjects")
public class Project {
private String ProjectName;
private List<String> TeamMates;
#DynamoDBHashKey(attributeName = "ProjectName")
public String getProjectname() {
return ProjectName;
}
public void setProjectname(String projectName) {
this.ProjectName = projectName;
}
public List<String> getTeammates() {
return TeamMates;
}
public void setTeammates(List teammates) {
this.TeamMates= TeamMates;
}
}
Lastly here is a photo of my table and the item that contains a String for the project description and a List of Strings for my TeamMates. I assumed because the table determined teamMates to be a list I should also create a List when returning teamMates.
http://i67.tinypic.com/2qnm58h.jpg
Help would be appreciated.
You need to set annotation above the getTeammates() method, similar to how you set it for the getProjectname() method.
For example,
#DynamoDBAttribute(attributeName = "teammates")

Separate database model from Network model

Im using GreenDAO and Volley. So I have the following problem: When I make a network request I need to parse with GSON so I have a model to represent entities retrieved from server and other model to represent the GreenDAO objects. Is there any way to only have 1 class per model to represent as a GSON and a Class of ORM?
class Product:
#SerializedName("id")
private String id;
#SerializedName("pictures")
private List<Picture> pictures;
get & set
class PersistentProduct:
private Long id;
private List<Picture> pictures;
/** To-many relationship, resolved on first access (and after reset). Changes to to-many relations are not persisted, make changes to the target entity. */
public List<PersistencePicture> getPictures() {
if (pictures == null) {
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
PersistencePictureDao targetDao = daoSession.getPersistencePictureDao();
List<PersistencePicture> picturesNew = targetDao._queryPersistenceProduct_Pictures(id);
synchronized (this) {
if(pictures == null) {
pictures = picturesNew;
}
}
}
return pictures;
}
First I thought to make a Interface, but when you retrieve the data from a DAO the DAO returns the class and not the interface, so I think cannot do in this way, the only solution I found is to make a "ProductUtils" that converts from a "PersistentProduct" to a "Product" and vice versa.
The most elegant way would be to implement a small extension for greendao, so that you can specify the serialized name during schema-creation.
For Example:
de.greenrobot.daogenerator.Property.java:
// in PropertyBuilder append these lines
public PropertyBuilder setSerializedName(String sname) {
// Check the sname on correctness (i.e. not empty, not containing illegal characters)
property.serializedName = sname;
return this;
}
// in Property append these lines
private String serializedName = null;
public boolean isSerialized() {
return serializedName != null;
}
In entity.ftl add this line after line 24 (after package ${entity.javaPackage};):
<#if property.serializedName??>
import com.google.gson.annotations.SerializedName;
</#if>
And after line 55 (after: <#list entity.properties as property>)
<#if property.serializedName??>
#SerializedName("${property.serializedName}")
</#if>
Afterwards you should be able to use you generated greendao-entity for volley with the following restrictions:
If you get a Product over network, nothing is changed in the db, yet. You have to call insertOrReplace().
If you get a Product from db and send it via network some undesired fields might be serialized (i.e. myDao and daoSession)
If you get a Product via network and call insertOrReplace() the "network"-Product will be persisted and a already existing Product will be replaced by it BUT the referenced entities won't get updated or persisted if insertOrReplace() isn't called for each of them!
If you get a Product via network and call insertOrReplace() for every referenced entity toMany-entities that were referenced by the db-Product are still referenced by the updated Product, although they are not listed in the updated Product. You have to call resetPictures() and getPictures() to get the correct list, which will contain all toMany()-entities references by either the original Product stored in DB or the updated Product from network.
Update addressing 2.
To prevent daoSession and myDao from being serialized, you can use the following ExclusionStrategy:
private static class TransientExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return (clazz.getModifiers() & java.lang.reflect.Modifier.TRANSIENT) != 0;
}
public boolean shouldSkipField(FieldAttributes f) {
return f.hasModifier(java.lang.reflect.Modifier.TRANSIENT);
}
}
Update addressing 1.,3. and 4.
As a fast solution you can add the following method in the KEEP-SECTIONS of your entity:
public void merge(DaoSession s) {
s.insertOrReplace(this);
// do this for all toMany-relations accordingly
for (Picture p : getPictures()) {
s.insertOrReplace(p);
newPics.add(p.getId());
}
resetPictures();
}
This will result in the original entity being updated and attached to the session and dao. Also every Picture that is references by the network-product will be persisted or updated. Pictures reference by the original entity, but not by the network-entity remain untouched and get merged into the list.
This is far from perfect, but it shows where to go and what to do. The next steps would be to do everything that is done in merge() inside one transaction and then to integrate different merge-methods into dao.ftl.
NOTE
The code given in this answer is neither complete nor tested and is meant as a hint on how to solve this. As pointed out above this solution still has some restrictions, that have to be dealt with.

JPA: Fetch data from DB instead of Persistance Context

I have a simple User Account application in which the user is able to change his details.
Updating the Database
The Managed Bean's method which takes the form parameters and calls the Service method:
public String changeDetails(){
Date date = DateUtil.getDate(birthDate);
Integer id = getAuthUser().getId();
UserDetail newDetails = new UserDetail(id, occupation, date, originCity, residenceCity, description);
EntityTransaction transaction = getTransaction();
userService.updateDetail(newDetails);
transaction.commit();
return null;
}
The Service Method:
public boolean updateDetail(UserDetail newDetails) {
boolean ok = true;
if (newDetails != null) {
UserDetail user = readDetail(newDetails.getId());
user.setOccupation(newDetails.getOccupation());
user.setOriginCity(newDetails.getOriginCity());
user.setResidenceCity(newDetails.getResidenceCity());
user.setBirth(newDetails.getBirth());
user.setDescription(newDetails.getDescription());
}
return ok;
}
Fetching data from DB
#PostConstruct
public void init(){
userService = new UserService();
sessionController.setAuthUser(userService.read(getAuthUser().getId()));
originCity = getAuthUser().getUserDetail().getOriginCity();
residenceCity = getAuthUser().getUserDetail().getResidenceCity();
occupation = getAuthUser().getUserDetail().getOccupation();
birthDate = DateUtil.getStringDate(getAuthUser().getUserDetail().getBirth());
description = getAuthUser().getUserDetail().getDescription();
}
The problem is that the behavior of this code is different. Sometimes I obtain the desired result: once I submit the new details and call the #PostConstruct init () the new details are printed. Some other times the old details are printed even though the DB entry is updated.
Conclusion: Sometimes the JPA brings me different result from what is in the DB. I guess that this results consist of data from the Persistance Context, data which isn't updated. Is there a way in which I can be sure that the JPA always brings the data directly from the DB? Or is there something I'm missing?
If you are using JPA 2 then #Cacheable(false) on your entity definition should make it read from the DB every time.
You mean is there a way to turn the cache off or empty it before an operation ?
emf.getCache().evictAll();

How to persist a JPA autogenerated value before commit?

Hi I am beginner on the JPA world, I have a question on the auto-generated id. We are using OpenJPA, My application requires that one operation which creates bunch of related objects must be inside a single transaction which will be part of global transaction (XA). I am struggling in get the auto-generated id and use it to set values in other object. Here is the snapshot:
#ENTITY
#Table(name="TDepart")
class Department{
private long id;
#GeneratedValue(strategy= GenerationType.TABLE)
public long getId();
}
//And some classes like
class Professor {
void setDepartmentId(long id);
}
Now I have a business operation:
void doSomething()
{
Department depart = new Department();
handleProfessors (depart);
handleStudent (depart);
//and someother rountines need to refer department
}
//sample code which will getId
void handleProfessors(Department depart)
{
Professor p = new Professor ();
p.setDepartmentId(depart.getId);
}
So the Department.getId() will be called several times. The doSomething() will be in a single managed transaction, but the GeneratedValue will use an unmanaged tx. Now may problem is: whenever the getId is called, it will return a new value, and when the department is final persisted, the id is the latest number, so all other objects refer to an non-exists department. Is there anyway to handle this so that the id is (kindof) persist?
I have a loose requirement solution, which will create an dummy department first and persist it, so the ID is not change. The code is similar to this:
void doSomething()
{
Department depart = createEmptyDepartment(); // always new tx so department is created;
try {
reallyDoSomehing(); // tx required so it is part of global tx
}
catch (SomeException e) {
removeEmptyDepartment(depart);
}
Now I do not know how I can set the tx for removeEmptyDepartment(), if is required it will use the global request so it will be rollback as well. If it is new tx it will cause a deadlock since reallyDoSomething() will lock the db row.
Please, give me some ideas on how to solve it.
Thanks,
Howard.
I don't fully understand your issue, but I'm thinking that rather than setting the departmentId in your professor class, you should be setting the Department instead
i.e.
void setDepartmentId(long id);
change to
void setDepartment(Department d);
The id components should be handled automatically by the entity manager

Categories

Resources