I'm getting in trouble trying to separate the business and the persistance in my project.
The skeleton does more or less something like:
private UsuarioBO findById(String idUsuario) {
Usuario dao = getUsuarioDao().findById(idUsuario);
return new UsuarioBO(dao);
}
private void save(UsuarioBO bo){
Usuario dao = bo.bo2dao();
getUsuarioDao().save(dao);
}
Some clarifications about the code above:
UsuarioBO is a business objet and Usuario is an entity mapped to a DB
table.
The new UsuarioBO(dao) is just a mapping method and the
bo.bo2dao() is also a mapping method, but it creates a new empty entity
Usuario.
As you can see, the entity created in both methods is erased of memory on method finish.
The problem goes when i try to do this:
UsuarioBO example = findById("whatever");
save(example);
When i do this, hibernate tells me "there's an existing entity with the same identifier", and thats true! (It's creating an entity on findById() and another on save() ).
The only solution i've found is to use the entity as "bo". I mean, bring the entity object to the service, do whatever modifications on the entity directly, and when i finish do the save sending the entity in spite of a BO.
I'm pretty sure there's a better way of doing so, but how?
EDIT:
this is the save(dao) method:
public void save(Usuario usuario) throws Exception {
try {
getSession().saveOrUpdate(usuario);
} catch (RuntimeException e) {
//error treatment
throw e;
}
}
this is the bo2dao() method (its inside the BO bean):
public Usuario bo2dao() throws Exception {
Usuario dao = new Usuario();
try {
dao.setId(this.id);
dao.setPassword(this.password);
//other similar fields...
dao.setLastLoginTime(this.lastLoginTime);
Role r = new Role();
r.setId(LoginHelper.getRoleId(this.role.getName()));
dao.setRole(r);
Status s = new Status();
s.setId(LoginHelper.getStatusId(this.status.getName()));
dao.setStatus(s);
} catch (Exception e) {
throw e;
}
return dao;
}
In this case, Role and Status are also entity beans connected with Usuario (one user can only have one role and one status).
getRoleId() and getStatusId() returns the correspondent id from the name (for example: "UNLOCKED" returns 1)
Related
Assume that "Project" is POJO. In service layer of my project, I wrote a function that is get a row from table.
#Override
public ProjectDto getAProject(Long id) {
try {
Project project = projectRepository.getOne(id);
if (project==null) {
throw new IllegalArgumentException("Project not found");
} else {
return modelMapper.map(project, ProjectDto.class);
}
} catch (EntityNotFoundException ex) {
throw new IllegalArgumentException("Project not found");
}
}
The function is working fine with already exist id values. But if I give non-exist value, an exception occur like following. Looks like "getOne()" function don't throw "EntityNotFoundException".
ModelMapper mapping errors: Error mapping com.issuemanagement.entity.Project to com.issuemanagement.dto.ProjectDto
that means the exception come from model mapper logic. Because "project" object filled with null values so couldn't map to DTO class. I modified the function as following to fix this.
#Override
public ProjectDto getAProject(Long id) {
boolean isExist = projectRepository.existsById(id);
if (isExist) {
Project project = projectRepository.getOne(id);
return modelMapper.map(project, ProjectDto.class);
} else {
throw new IllegalArgumentException("Project not found");
}
}
but in this way the program goes to DB for two times. I don't like it. How can I do this operation with just one transaction?
BTW, if I try to run "toString()" function of "project", it throw "EntityNotFoundException" but it's looks like not official way. or it is? I hope there should be a boolean variable in somewhere.
getOne() on JpaRepository will call getReference() on EntityManager under the hood which will return an instance whose state is lazily fetch .The EntityNotFoundException will not throw immediately but will only be thrown when its state is accessed at the first time .
It is normally used in the case that when you need to configure a #ManyToOne relationship for an entity (Let say configure a Department for an Employee) but you just have the ID of the related entity.(e.g. DepartmentId) . Using getOne() allows you to get a Department proxy instance such that you do not really need to query the database to get the actual Department instance just for setting up its relationship for an Employee.
In your case , you should use findById() which will return an empty Optional if the instance does not exist:
#Override
public ProjectDto getAProject(Long id) {
Project project = projectRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("Project not found"));
return modelMapper.map(project, ProjectDto.class);
}
I have an issue with a webapp running in tomcat where I have an abstract DAO class with a method called all() which returns all entities from the database or JPA cache. It seems to correctly return the entities on the initial call but subsequent calls don't reflect updates happening from separate UI calls which will use the entity managers find method to find the specific entity from the list, update the relative fields and commit that. When I view that list via the same all() method later I still see the original values. If I make another update in the logs I can see the value changing from the correct value(not the original value) to the updated value and the logs shows those updates happening correctly each time.
I'm using guice for injection. I've played around with the logging and can see the same hashcode on the entity manager being used throughout a request but different for each request. I've played with the following the persistance.xml file which didn't seem to help either...
<property name="eclipselink.cache.shared.default" value="false" />
<shared-cache-mode>NONE</shared-cache-mode>
I can't see why my all() won't return updated results, I've also tried adding code to find the specific entity I'm updating in the list then replaced it by calling the following...
entity = em.find(Class.class, id)
This seemed to fix the issue on that particular entity so it appears my query is reusing old.
Here's a snippet from my DAO class
private final Provider<EntityManager> emP;
protected EntityManager em(boolean useTransaction) throws DaoException {
return useTransaction ? begin() : emP.get();
}
public List<T> all() throws DaoException {
EntityManager em = em(false);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(eClass);
Root<T> from = cq.from(eClass);
return em.createQuery(cq.select(from)).getResultList();
}
public T save(T t) throws DaoException {
return save(t, em(true));
}
protected T save(T t, EntityManager em) throws DaoException {
if (Objects.isNull(t)) {
throw new DaoException("can't save null object: " + getDaoClass(), new NullPointerException());
}
T saved;
if (t.getId() > 0) {
saved = em.merge(t);
} else {
em.persist(t);
saved = t;
}
autoCommit();
return saved;
}
protected void autoCommit() throws DaoException {
if (autoCommit) {
commit();
}
}
public void commit() throws DaoException {
EntityManager em = emP.get();
if (!em.getTransaction().isActive()) {
throw new DaoException("transaction isn't active, unable to commit");
}
try {
em.getTransaction().commit();
} catch (IllegalStateException e) {
throw new DaoException("transaction not active", e);
} catch (RollbackException e) {
throw new DaoException("commit rolled back", e);
}
}
So I'm wondering if anyone has any insights on why this might be happening or have any suggestions on what else I can try to check?
So I found the cause of the issue I was having. I was using the ElementCollection annotation in my entities when referencing lists. I removed the annotation and replaced it with a JoinTable and OneToMany annotations and things are working correctly.
The issue I had was that the entity would be stored in the database fine and I was updating that entity as expected but JPA had embedded the list of entities where it was referenced.
So I was seeing the embedded list returned each time which was not the actual entity I had updated. My entities are now using proper join tables instead of embedded objects and everything is behaving as expected.
I have got a Springboot Application and a Oracle DB with lots of PL/SQL Procedures and these change the state of the DB all the Time.
So now I want to change a loaded entity an want to save it. If the entitystate of the entitymanager and the state of the db is equal everything works fine. But in some cases they are not equal. So if I load an entity and make some changes an druring this a PL/SQL Procedure changes the DB Table. If I save the Entity I will get an Execption of course. So I tried to catch the Exception and then in the catch block I want to refresh the Entity before saving it. But I still get an Exception. Is the Transaction not jet finished? How can I handle this Problem?
I hope the example code explains a little bit.
#RestController
#RequestMapping("/*")
public class FacadeController {
...
#ResponseStatus(HttpStatus.OK)
#RequestMapping( value= "/test4" , method=RequestMethod.GET)
public String test4(){
Unit unit = unitSvice.loadUnit(346497519L);
List<UnitEntry> entries = unit.getEntries();
for (UnitEntry g : entries) {
if (g.getUnitEntryId == 993610345L) {
g.setTag("AA");
g.setVersion(g.getVersion() + 1);
g.setstatus("SaveOrUpdate");
}
}
//<-- DB Table changed entity managed by entitymanger and DB Table
// are no langer equal.
try {
unitSvice.updateUnit(unit , false);
}catch(DataAccessException | IllegalArgumentException e) {
unitSvice.updateUnit(unit , true);
}
...
}
}
#Service("unitSvice")
public class UnitSvice {
#Autowired
private UnitDao repoUnit;
#Transactional
public Unit loadUnit(Long _id) {
Unit unit = repoUnit.findOne(_id);
return unit;
}
#Transactional
public void updateUnit(Unit unit, boolean _withrefrsh ) {
if(_withrefrsh) {
getEntityManager().refresh(unit.getId());
}
repoUnit.save(unit);
}
}
I hope, anyone can help me.
Thanks
yes the problem is ..when you call load all method which is transactional method where entities became detached from session/entitymanager when you are returning from that method.. so,next you are trying to persist detached object. That's why you get exception.
so probably you can use session.update() or session.merge() to save the new update into database.
I'm writing tests for my Dao Spring application. I found out that when I delete not saved items no exception is invoked as I'd expect, I've got no idea why.
Model:
#Entity
public class Ingredient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String condition;
private int quantity;
public Ingredient() {
}
}
The Dao implementation:
#Override
public void delete(Object o) throws DaoException {
try {
Session session = mSessionFactory.openSession();
session.beginTransaction();
session.delete(o);
session.getTransaction().commit();
session.close();
} catch (Exception ex) {
throw new DaoException(ex, String.format("Problem deleting %s object (delete method).", o));
}
}
And my test, expecting DaoException:
#Test
public void testDeleteNotSavedThrowsDaoException() throws Exception {
Ingredient ingredient = new Ingredient("Not saved ingredient","", 1);
ingredientDao.delete(ingredient);
}
Hibernate's Javadoc for Session#delete(Object) states:
Remove a persistent instance from the datastore. The argument may be an instance associated with the receiving Session or a transient instance with an identifier associated with existing persistent state.
So it's not an error to pass in a transient entity (as you do). Also, the Session#delete method does not declare any exceptions, so it's not defined what happens when you pass in an entity with an ID that does not exist in the DB. As you can see - nothing happens - you requested the entity not to exist in the DB, it's not there to start with, so no reason to throw an exception (according to Hibernate, at least).
Compare this to the basic SQL DELETE FROM X WHERE ID = Y - this does not check if a record with ID=Y exists, it will succeed either way (updating 0 or 1 rows).
UPDATE after realizing the passed in transient entity has null ID.
I've dug into the sources of Hibernate 5.2.2 Session and it seems that if the passed in entity has no ID, no DELETE query is even performed on that entity's table.
See DefaultDeleteEventListener#onDelete(DeleteEvent, Set):
if (ForeignKeys.isTransient( persister.getEntityName(), entity, null, source ) ) {
// yes, your entity is transient according to ForeignKeys.isTransient
deleteTransientEntity( source, entity, event.isCascadeDeleteEnabled(), persister, transientEntities );
return;
}
Now
protected void deleteTransientEntity(
EventSource session,
Object entity,
boolean cascadeDeleteEnabled,
EntityPersister persister,
Set transientEntities) {
LOG.handlingTransientEntity(); // Only log it
if ( transientEntities.contains( entity ) ) {
LOG.trace( "Already handled transient entity; skipping" );
return;
}
transientEntities.add( entity );
// Cascade deletion to related entities
cascadeBeforeDelete( session, persister, entity, null, transientEntities );
cascadeAfterDelete( session, persister, entity, transientEntities );
}
this will just print "HHH000114: Handling transient entity in delete processing" in the logs and do nothing with the entity (however, it will cascade the deletion to the related entities if there are any - not your case).
So again - it's OK to pass in a transient entity without an ID - it will simply not run a DELETE on the DB.
And that was an answer, Adam, there was no exception, because id of my new, not saved item was null. When I set id to value which not persist in DB exception was thrown.
I am trying to learn JDO (and at the same time its GAE and Spring intricacies) by creating a small web app, and am having trouble getting updated domain objects to persist back to the database. I initially grab the entity from the DB and detach it so that I can show it to the user and allow them to change it. Once the user has made the changes and posts the form back to the app, I again grab the entity from the DB (detached), update its properties, and then call a pm.makePersistent(). The abbreviated code is as follows:
User Domain Object:
#PersistenceCapable(detachable="true")
public class User extends BaseEntity {
#Persistent
private String firstName = "";
#Persistent
private String middleInitial = "";
#Persistent
private String lastName = "";
}
DAO Read Method:
public User read(Key key) throws DataException {
PersistenceManager pm = PMF.get().getPersistenceManager();
User pkg, detached = null;
try {
pkg = (User) pm.getObjectById(User.class, key);
detached = pm.detachCopy(pkg);
detached.setIsAlreadyInDB(true);
}
catch (Exception e) {
throw new DataException("An error occured trying to read the User object. Details:\n" + e.getMessage());
}
finally {
pm.close();
}
return detached;
}
DAO Update Method:
private void update(User pkg) throws DataException {
PersistenceManager pm = PMF.get().getPersistenceManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(pkg);
tx.commit();
}
finally {
if (tx.isActive()) tx.rollback();
pm.close();
}
}
Now when I get down into the update method, I've proven to myself that I'm working with just the same object from my read via inspecting its hashCode(), I've changed a value using the domain object's setter method, I've even printed the changed value to the console to make sure it's getting done, and JDOHelper.isDirty() still returns false, and therefore none of the changes get persisted back to the database.
Any thoughts on what I'm missing or if I'm approaching this from the wrong angle? Thank you for helping out a JDO beginner!
JDOHelper.isDirty is for managed objects. A detached object is not managed. DataNucleus provides a helper method of its own to get the dirty fields while detached since the logic is implementation-specific
String[] dirtyFieldNames = NucleusJDOHelper.getDetachedObjectDirtyFields(obj, pm);