I have problem with cloning entity in my application created with play framework 2.1.
When I'm runing method below I get RollbackException: Error while committing the transaction.
My question is: What is a proper way to clone entity using jpa in playframework?
#play.db.jpa.Transactional
public static Result edit(final Long id) {
final Client client = Client.findById(id);
if (client == null) {
return notFound(String.format("Client %s does not exist.", id));
}
if(client.address.id == client.corrAddress.id){
client.corrAddress = client.address.clone();
}
Form<Client> filledForm = CLIENT_FORM.fill(client);
return ok(form.render(filledForm, "Edycja danych klienta"));
}
Address clone method:
public Address clone(){
return new Address(this.street, this.postCode, this.city);
}
UPDATE:
I made some changes in code. I don't use method clone now, as #Christopher Hunt suggested.
I also got another error.
I made method cascade in Client class:
public void cascade(){
if(address.equals(corrAddress)){
if(address.id != null && corrAddress.id != null
&& address.id.equals(corrAddress.id)){
address.client = this;
address.corrClient = this;
address.saveOrUpdate();
corrAddress = null;
} else {
address.client = this;
address.corrClient = this;
address.saveOrUpdate();
corrAddress = null;
}
} else {
if(address.id != null && corrAddress.id != null
&& address.id.equals(corrAddress.id)){
System.out.println("TEST");
address.client = this;
address.corrClient = null;
address.saveOrUpdate();
String street = this.corrAddress.street;
String postCode = this.corrAddress.postCode;
String city = this.corrAddress.city;
corrAddress = null;
/** CODE BELOW CAUSES ERROR: **/
corrAddress = new Address(street, postCode, city);
corrAddress.client = null;
corrAddress.corrClient = this;
corrAddress.saveOrUpdate();
/** CODE ABOVE CAUSES ERROR: **/
} else {
address.client = this;
address.saveOrUpdate();
corrAddress.corrClient = this;
corrAddress.saveOrUpdate();
}
}
}
definition of address references in Client class:
#Valid
#OneToOne(mappedBy="client", orphanRemoval=true, fetch = FetchType.EAGER)
public Address address;
#Valid
#OneToOne(mappedBy="corrClient", orphanRemoval=true, fetch = FetchType.EAGER)
public Address corrAddress;
Now I'm getting error:
play.api.Application$$anon$1: Execution
exception[[PersistenceException: org.hibernate.HibernateException:
More than one row with the given identifier was found: 2, for class:
models.Address]] at
play.api.Application$class.handleError(Application.scala:289)
~[play_2.10.jar:2.1.0] at
play.api.DefaultApplication.handleError(Application.scala:383)
[play_2.10.jar:2.1.0] at
play.core.server.netty.PlayDefaultUpstreamHandler$$anon$2$$anonfun$handle$1.apply(PlayDefaultUpstreamHandler.scala:132)
[play_2.10.jar:2.1.0] at
play.core.server.netty.PlayDefaultUpstreamHandler$$anon$2$$anonfun$handle$1.apply(PlayDefaultUpstreamHandler.scala:128)
[play_2.10.jar:2.1.0] at
play.api.libs.concurrent.PlayPromise$$anonfun$extend1$1.apply(Promise.scala:113)
[play_2.10.jar:2.1.0] at
play.api.libs.concurrent.PlayPromise$$anonfun$extend1$1.apply(Promise.scala:113)
[play_2.10.jar:2.1.0] javax.persistence.PersistenceException:
org.hibernate.HibernateException: More than one row with the given
identifier was found: 2, for class: models.Address at
org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1360)
~[hibernate-entitymanager-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1288)
~[hibernate-entitymanager-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1294)
~[hibernate-entitymanager-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:877)
~[hibernate-entitymanager-4.1.1.Final.jar:4.1.1.Final] at
models.clients.Client.update(Client.java:58) ~[na:na] at
models.clients.Client.saveOrUpdate(Client.java:110) ~[na:na] Caused
by: org.hibernate.HibernateException: More than one row with the given
identifier was found: 2, for class: models.Address at
org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:104)
~[hibernate-core-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.loader.entity.EntityLoader.loadByUniqueKey(EntityLoader.java:161)
~[hibernate-core-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2209)
~[hibernate-core-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:661)
~[hibernate-core-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.type.EntityType.resolve(EntityType.java:441)
~[hibernate-core-4.1.1.Final.jar:4.1.1.Final] at
org.hibernate.type.EntityType.replace(EntityType.java:298)
~[hibernate-core-4.1.1.Final.jar:4.1.1.Final]
Important is that I don't want to operate with 2 element list. I want 2 fileds (address, corrAddress) in my Client class.
Related
I know that in Java a method can return only one return type... But if there is any possiblity to this, kindly let me know. From the below method I am trying to return a list if condition satisfies else i am trying to return an error message.
Here is my code:
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return "Incorrect loginId..Please enter valid loginId";
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return companyList;
} else {
return "You are not Authorized";
}
} else {
return "Incorrect Password";
}
Yes its possible, create a custom Exception say 'MyAppException' and throw that exception with the error message you want.
Write your logic in a try{}catch block and throw the exception in catch so that the response has the error message
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) throws MyAppppException
{
try
{
//your logic which throws error
return companyList;
}
catch( final MyAppException we )
{
throw new MyAppException("User not found", HttpStatus.NOT_FOUND);
}
}
Refer this link
https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java
You can achieve this by creating a new presenter Class which contains List and status of type String and change the return type of getCompanies method to presenter class like
public CompaniesPresenter getCompanies()
And your CompaniesPresenter class should look like
public class CompaniesPresenter {
private List<CompanyMaster> companyMaster;
private string status;
//default constructor
public CompaniesPresenter(){
}
//parameterized constructor to return only string in exception case
public CompaniesPresenter(Stirng status){
this.status = status;
}
//parametirized constructor to return success case
public CompaniesPresenter(List<CompanyMaster> companyMaster, Stirng status){
this.companyMaster = companyMaster;
this.status = status;
}
//getters and setters
}
This is how your updated method lokks like
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public CompaniesPresenter getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return new CompaniesPresenter("Incorrect loginId..Please enter valid loginId");
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return new CompaniesPresenter(companyList,"success");
} else {
return new CompaniesPresenter("You are not Authorized");
}
} else {
return new CompaniesPresenter("Incorrect Password");
}
This is not tested please make sure for any compilation errors
vavr's Either class would be a good choice.
The usage of custom exception is most reasonable solution. However, creating custom exception for just one case is not ideal always.
Another solution is to return empty List from your method, check if the List is empty in your servlet (or wherever you are invoking this method from), and show error message there.
It seems like you want to return multiple error messages for different cases. In this case, custom exception is recommended solution. If you don't like custom exceptions, you can return List<Object> and populate error message as the first element in the list. In the place where this List is obtained, check if the first element is instanceOf String or CompanyMaster. Based on what it is, you can perform your operations. This is a weird but possible solution (only if you don't like custom exceptions).
You need to understand the problem first. You are mixing two things here, first authorization, does the user has correct privileges to get company details, second giving the company details itself. Let's understand the first problem when a user tries to access "/getcompanies" endpoint will you let him in if does not have access, in REST world your security model should take care of it. I would use spring security to achieve this. My recommendation would be to explore on "interceptor" and solve the problem of invalid user. This will make your other problem easy as your "/getcompanies" endpoint can focus only on getting the details and return it (SRP).
Need guideline -
How to do hard delete when no reference is available and do soft delete when reference is available, this operation should be performed in a single method itself.
E.g.
I have 1 master table and 3 transactional tables and the master reference is available in all 3 transactional tables.
Now while deleting master row - I have to do the following: If master reference is available then update the master table row and if no master ref. is available delete the row.
I tried following so far.
Service Implementation -
public response doHardOrSoftDelete(Employee emp) {
boolean flag = iMasterDao.isDataExist(emp);
if(flag) {
boolean result = iMasterDao.doSoftDelete(emp);
} else {
boolean result = iMasterDao.doHardDelete(emp);
}
}
Second Approach:
As we know that while deleting a record if the reference is available then it throws ConstraintViolationException so simply we can catch it and check that caught exception is of type ConstraintViolationException or not, if yes then call doSoftDelete() method and return the response. So here you don't need to write method or anything to check the references. But I'm not sure whether it is the right approach or not. Just help me with it.
Here is what I tried again -
public Response deleteEmployee(Employee emp) {
Response response = null;
try{
String status= iMasterDao.deleteEmployeeDetails(emp);
if(status.equals("SUCCESS")) {
response = new Response();
response.setStatus("Success");
response.setStatusCode("200");
response.setResult("True");
response.setReason("Record deleted successfully");
return response;
}else {
response = new Response();
response.setStatus("Fail");
response.setStatusCode("200");
response.setResult("False");
}
}catch(Exception e){
response = new Response();
Throwable t =e.getCause();
while ((t != null) && !(t instanceof ConstraintViolationException)) {
t = t.getCause();
}
if(t instanceof ConstraintViolationException){
boolean flag = iMasterDao.setEmployeeIsDeactive(emp);
if(flag) {
response.setStatus("Success");
response.setStatusCode("200");
response.setResult("True");
response.setReason("Record deleted successfully");
}else{
response.setStatus("Fail");
response.setStatusCode("200");
response.setResult("False");
}
}else {
response.setStatus("Fail");
response.setStatusCode("500");
response.setResult("False");
response.setReason("# EXCEPTION : " + e.getMessage());
}
}
return response;
}
Dao Implementation -
public boolean isDataExist(Employee emp) {
boolean flag = false;
List<Object[]> tbl1 = session.createQuery("FROM Table1 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl1.isEmpty() && tbl1.size() > 0) {
flag = true;
}
List<Object[]> tbl2 = session.createQuery("FROM Table2 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl2.isEmpty() && tbl2.size() > 0) {
flag = true;
}
List<Object[]> tbl3 = session.createQuery("FROM Table3 where emp_id=:id")
.setParameter("id",emp.getId())
.getResultList();
if(!tbl3.isEmpty() && tbl3.size() > 0) {
flag = true;
}
return flag;
}
public boolean doSoftDelete(Employee emp) {
empDet = session.get(Employee.class, emp.getId());
empDet .setIsActive("N");
session.update(empDet);
}
public boolean doHardDelete(Employee emp) {
empDet = session.get(Employee.class, emp.getId());
session.delete(empDet);
}
No matter how many transactional tables will be added with master tbl reference, my code should do the operations(soft/hard delete) accordingly.
In my case, every time new transactional tables get added with a master reference I've do the checks, so Simply I want to skip the isDataExist() method and do the deletions accordingly, how can I do it in a better way?
Please help me with the right approach to do the same.
There's a lot of repeated code in the body of isDataExist() method which is both hard to maintain and hard to extend (if you have to add 3 more tables the code will double in size).
On top of that the logic is not optimal as it will go over all tables even if the result from the first one is enough to return true.
Here is a simplified version (please note that I haven't tested the code and there could be errors, but it should be enough to explain the concept):
public boolean isDataExist(Employee emp) {
List<String> tableNames = List.of("Table1", "Table2", "Table3");
for (String tableName : tableNames) {
if (existsInTable(tableName, emp.getId())) {
return true;
}
}
return false;
}
private boolean existsInTable(String tableName, Long employeeId) {
String query = String.format("SELECT count(*) FROM %s WHERE emp_id=:id", tableName);
long count = (long)session
.createQuery(query)
.setParameter("id", employeeId)
.getSingleResult();
return count > 0;
}
isDataExist() contains a list of all table names and iterates over these until the first successful encounter of the required Employee id in which case it returns true. If not found in any table the method returns false.
private boolean existsInTable(String tableName, Long employeeId) is a helper method that does the actual search for employeeId in the specified tableName.
I changed the query to just return the count (0 or more) instead of a the actual entity objects as these are not required and there's no point to fetch them.
EDIT in response to the "Second approach"
Is the Second Approach meeting the requirements?
If so, then it is a "right approach" to the problem. :)
I would refactor the deleteEmployeeDetails method to either return a boolean (if just two possible outcomes are expected) or to return a custom Enum as using a String here doesn't seem appropriate.
There is repeated code in deleteEmployeeDetails and this is never a good thing. You should separate the logic which decides the type of the response from the code that builds it, thus making your code easier to follow, debug and extend when required.
Let me know if you need a code example of the ideas above.
EDIT #2
Here is the sample code as requested.
First we define a Status enum which should be used as return type from MasterDao's methods:
public enum Status {
DELETE_SUCCESS("Success", "200", "True", "Record deleted successfully"),
DELETE_FAIL("Fail", "200", "False", ""),
DEACTIVATE_SUCCESS("Success", "200", "True", "Record deactivated successfully"),
DEACTIVATE_FAIL("Fail", "200", "False", ""),
ERROR("Fail", "500", "False", "");
private String status;
private String statusCode;
private String result;
private String reason;
Status(String status, String statusCode, String result, String reason) {
this.status = status;
this.statusCode = statusCode;
this.result = result;
this.reason = reason;
}
// Getters
}
MasterDao methods changed to return Status instead of String or boolean:
public Status deleteEmployeeDetails(Employee employee) {
return Status.DELETE_SUCCESS; // or Status.DELETE_FAIL
}
public Status deactivateEmployee(Employee employee) {
return Status.DEACTIVATE_SUCCESS; // or Status.DEACTIVATE_FAIL
}
Here is the new deleteEmployee() method:
public Response deleteEmployee(Employee employee) {
Status status;
String reason = null;
try {
status = masterDao.deleteEmployeeDetails(employee);
} catch (Exception e) {
if (isConstraintViolationException(e)) {
status = masterDao.deactivateEmployee(employee);
} else {
status = Status.ERROR;
reason = "# EXCEPTION : " + e.getMessage();
}
}
return buildResponse(status, reason);
}
It uses two simple utility methods (you can make these static or export to utility class as they do not depend on the internal state).
First checks if the root cause of the thrown exception is ConstraintViolationException:
private boolean isConstraintViolationException(Throwable throwable) {
Throwable root = throwable;
while (root != null && !(root instanceof ConstraintViolationException)) {
root = root.getCause();
}
return root != null;
}
And the second one builds the Response out of the Status and a reason:
private Response buildResponse(Status status, String reason) {
Response response = new Response();
response.setStatus(status.getStatus());
response.setStatusCode(status.getStatusCode());
response.setResult(status.getResult());
if (reason != null) {
response.setReason(reason);
} else {
response.setReason(status.getReason());
}
return response;
}
If you do not like to have the Status enum loaded with default Response messages, you could strip it from the extra info:
public enum Status {
DELETE_SUCCESS, DELETE_FAIL, DEACTIVATE_SUCCESS, DEACTIVATE_FAIL, ERROR;
}
And use switch or if-else statements in buildResponse(Status status, String reason) method to build the response based on the Status type.
I am recently working on a task where I am refactoring an old web project. First I tried to run the project on my local weblogic instance before starting refactoring.
While I try to deploy the application I got a hibernate exception as follows ==>
Caused By: org.hibernate.AnnotationException: Unknown Id.generator: unique-id
at org.hibernate.cfg.BinderHelper.makeIdGenerator(BinderHelper.java:428)
at org.hibernate.cfg.AnnotationBinder.bindId(AnnotationBinder.java:1901)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1279)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:754)
at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:546)
Truncated. see log file for complete stacktrace
The exception above implying that it couldn't find the id generator named "unique-id" . Then I dig in to the code and see that the id field in the entity is string. and there is a custom ID generator class that is implementing hibernate IdentifierGenerator class where it is calling a db function for generating alphanumeric id for each time it is called. Below is piece of code in the entity class where it defines the type of id generation.
#Id
#Column(name = "ID", nullable = false, length = 18)
#GenericGenerator(name = "unique-id", strategy = "com.common.entity.RowIdGenerator")
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "unique-id")
private String id;
And here is the RowIdGenerator class for generating alphanumeric id.
public class RowIdGenerator implements IdentifierGenerator
{
private final static String SQL_TEXT = "SELECT TCC.F_ROW_ID_GEN FROM DUAL";
#Override
public Serializable generate(SessionImplementor sessionImplemetor, Object object) throws HibernateException
{
return this.getNextNumber(sessionImplemetor);
}
private String getNextNumber(SessionImplementor session)
{
try
{
ResultSet rs = null;
PreparedStatement statement = null;
try
{
statement = session.getBatcher().prepareSelectStatement(SQL_TEXT);
rs = statement.executeQuery();
String nextValue = null;
if (rs.next())
nextValue = rs.getString(1);
if (nextValue == null)
throw new HibernateException("is is null.");
return nextValue;
}
finally
{
if (rs != null)
rs.close();
if (statement != null)
session.getBatcher().closeStatement(statement);
}
} catch (SQLException sqle)
{
throw JDBCExceptionHelper.convert(session.getFactory().getSQLExceptionConverter(), sqle, "could not fetch initial value for increment generator", SQL_TEXT);
}
}
}
What may cause this error? I see that the generator named "unique-id" is defined and annotated. As far as I know there is no error in the code, maybe it is because of the configuration of my local weblogic instance but Any comment and advice would be appreciated.
regards
You can auto generate only number as ids.Auto generation for other types like String is not supported by hibernate
I've a question on hibernate operation: update.
Here a bit of code:
Campaign campaign = campaignDAO.get(id);
campaign.setStatus(true);
campaignDAO.update(campaign);
If I just have all the data of the campaign object, is there any way to perform an update without perform the first select (campaignDAO.get(id)) ?
Thanks,
Alessio
HQL will definitely help you.
In order to maintain the separation of concerns, you can add a more specialized method in you DAO object:
public void updateStatusForId(long id, boolean status){
//provided you obtain a reference to your session object
session.createQuery("UPDATE Campaign SET status = " + status + " WHERE id = :id").setParameter("id", id).executeUpdate();
//flush your session
}
Then you could simply call this method from your business method. You can check the generated SQL statements inside the logs of your app by setting the show_sql hibernate property to true.
You can use session.load(). It will not hit the database. Here you can find its details and example code.
I had worte a extension to solve this issue in Nhibernate
how to use!
first of all you need enable dynamic-update="true"
using (ISession session = sessionFactory.OpenSession())
{
Customer c1 = new Customer();
c1.CustomerID = c.CustomerID;
session.Mark(c1);
// c1.Name = DateTime.Now.ToString();
c1.Phone = DateTime.Now.ToString();
//需要开启动态更新
session.UpdateDirty(c1);
session.Flush();
}
UpdateExtension.cs
public static class UpdateExtension
{
static readonly Object NOTNULL = new Object();
public static void UpdateDirty<TEntity>(this ISession session, TEntity entity)
{
SessionImpl implementor = session as SessionImpl;
EntityEntry entry = implementor.PersistenceContext.GetEntry(entity);
if (entry == null)
{
throw new InvalidOperationException("找不到对应的实例,请先使用Mask方法标记");
}
IEntityPersister persister = entry.Persister;
// 如果某列不可以为空,新的Entity里也不想更新他。
// 那么LoadState 里的值应该和Entity 中的值相同
Object[] CurrentState = entry.Persister.GetPropertyValues(entity, EntityMode.Poco);
Object[] LoadedState = entry.LoadedState;
int[] dirtys = persister.FindDirty(CurrentState
, LoadedState
, entity
, (SessionImpl)session);
if (dirtys == null || dirtys.Length == 0)
{
return;
}
persister.Update(entry.Id
, CurrentState
, dirtys
, true
, LoadedState
, entry.Version
, entity
, entry.RowId
, (SessionImpl)session);
implementor.PersistenceContext.RemoveEntry(entity);
implementor.PersistenceContext.RemoveEntity(entry.EntityKey);
session.Lock(entity, LockMode.None);
// 防止(implementor.PersistenceContext.EntityEntries.Count == 0)
}
public static void Mark<TEntity>(this ISession session, TEntity entity)
{
session.Lock(entity, LockMode.None);
}
}
here is update sql
command 0:UPDATE Customers SET Phone = #p0 WHERE CustomerID = #p1;#p0 = '2014/12/26 0:12:56' [Type: String (4000)], #p1 = 1 [Type: Int32 (0)]
Only update Phone column .
event Name property can not be null. we can work very well.
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"