Remote EJB and JPA #Version - java

A multi-tiered application that I am developing accesses its database through a Payara application server. Originally I was using webservices (jax-ws) to provide the access. That decision was prompted by the ease of creating the services by using the #WebService and #Stateless annotations on my entity facade classes.
Due to some limitations of webservices (things like equals and hashCode methods not being created in the webservice interface), I decided to try to use EJB's to accomplish the same functionality. Using the webservices I was able to successfully perform all CRUD functionality on all of the database entities. All of my entity classes extend an AbstractEntity class, which is annotated #MappedSuperClass.
#MappedSuperclass
public abstract class AbstractEntity {
#Column(name = "UUID")
#Basic
private String uuid;
#Version
#Access(AccessType.FIELD)
#Column(name = "Revision")
#Basic
private long revision;
public AbstractEntity() {
uuid = UUID.randomUUID().toString();
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
/**
* #return entity's database primary key if persisted
*/
public abstract Long getId();
public long getRevision() {
return revision;
}
public void setRevision(long revision) {
this.revision = revision;
}
/**
* #return true if this entity is persisted to database, false
otherwise.
*/
public boolean isPersisted() {
return getId() != null;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AbstractEntity) {
return this.uuid.equals(((AbstractEntity) obj).uuid);
}
return false;
}
#Override
public int hashCode() {
return uuid.hashCode();
}
}
The client application correctly looks up the remote interfaces through JNDI, and I'm able to run my methods to query the data and return result lists exactly the same as I can using webservices. The problem, however, is in the version number and uuid that are returned with each entity instance. In all cases, the revision number that is returned is 0, and the revision and uuid don't match the revision and uuid stored in the database. I have verified that the result of my query on the server contains entities that have the correct version numbers, but when the entities get to the client, all of them are set to 0. Of course, if I make any changes to the entity on the client and then try to update the entity, I get an optimistic locking exception on the update method.
Does this have something to do with the entities being detached from the database? The update method is:
#Override
public ShiplistItem updateShipList(ShiplistItem shipitem) {
LOGGER.log(Level.INFO, "Entering updateShipList.");
LOGGER.log(Level.INFO, "shipitem: {0}", shipitem.toString());
if (shipitem.isPersisted()) {
return em.merge(shipitem);
} else {
em.persist(shipitem);
return shipitem;
}
}
I don't understand why the webservice would return the entities correctly, but the ejb seems to neglect the stored values for revision and uuid.
All help is appreciated!!
EDIT - Entity class snippet
public class ShiplistItem extends AbstractEntity implements
Serializable, Comparable<ShiplistItem> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private Long id;
....
EDIT #2
#Override
public List<ShiplistItem> listShipListByDate(String date) {
Query query = em.createQuery("SELECT s from ShiplistItem s
where s.shipdate = :shipdate", ShiplistItem.class)
.setParameter("shipdate", date);
LOGGER.log(Level.INFO, "SQL: {0}.", query.toString());
List<ShiplistItem> result = new ArrayList<>();
try {
result = (List<ShiplistItem>) query.getResultList();
} catch (Exception ex) {
LOGGER.log(Level.INFO, "No shipping list for date {0}", date);
}
for (ShiplistItem shiplistItem : result) {
LOGGER.log(Level.INFO, "revision: {0}",shiplistItem.getRevision());
}
LOGGER.log(Level.INFO, "shiplist size: {0}", result.size());
return result;
}

Related

Spring + MySQL records being duplicated instead of updated

In my project I'm migrating data from SQLite db into MySQL db. The problem is that when I migrate multiple times, the already existing records are being duplicated, instead of being updated(in case there are any changes in SQLite, otherwise no action is expected).
I'm using JpaRepository.saveAll(), which internally invokes save() for each entity:
#Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
Then isNew() is invoked but I'm not sure how it compares records in the target db to the ones that are currently being migrated and if that's where the problem occurs at all.
Do you have any suggestions to solving my problem?
That's my entity:
#Entity
#Table(name = "movies")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
-- properties
-- constructors/getters/setters/equals/hashcode
}
That's the service that migrates movies(SQLiteMovie is the entity extracted from SQLite, which is then mapped to (MySQL)Movie):
#Service
public class MovieMigrationServiceImpl implements MovieMigrationService {
private static final int MOVIE_TYPE = 1;
private final MovieMapper movieMapper;
private final MovieService movieService;
private final SQLiteMovieService sqliteMovieService;
public MovieMigrationServiceImpl(MovieMapper movieMapper, MovieService movieService,
SQLiteMovieService sqliteMovieService) {
this.movieMapper = movieMapper;
this.movieService = movieService;
this.sqliteMovieService = sqliteMovieService;
}
#Override
public String migrateMovies() {
Collection<SQLiteMovie> sqliteMovies = sqliteMovieService.getAllByMetadataType(MOVIE_TYPE);
Collection<Movie> movies = mapMovies(sqliteMovies);
movieService.saveMovies(movies);
return movies.size() + " movies migrated successfully!";
}
private Collection<Movie> mapMovies(Collection<SQLiteMovie> sqliteMovies) {
return sqliteMovies.stream()
.map(movieMapper::toMovie)
.collect(Collectors.toList());
}
}
And finally that's the structure of the table in MySQL:
Thanks in advance!

Why invoking of most JPA Repositpry methods through Class Implementing ContraintValidator during Save causing HH000099: Assertion Failure?

I have a simple field level custom validator that implements ContraintValidator. It makes use of JPA Repository to call
1. findById(), and
2. count()
within isValid method.
Call to findById() is successful. However, invoking count() method causes an error,
"ERROR org.hibernate.AssertionFailure - HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id entry (don't flush the Session after an exception occurs) org.hibernate.AssertionFailure: null id"
I have tried switching JPA Repository just to check if it was something specific for that repository but the behaviour is same across all repositories.
I have tried making calls to other JPA methods such as findIfExists, findAll, but all of them are causing same error.
The issue is that it complains about null id, when it is expected to be blank as it is call to Save.
public class CountValidator implements ConstraintValidator<Counter, String> {
private AreaRepository areaRepository;
public CountValidator(AreaRepository areaRepository) {
this.areaRepository = areaRepository;
}
#Override
public void initialize(Counter constraint) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
boolean isValid = false;
try {
// Simple check to see of repository is reachable (TO BE DELETED)
Area area = areaRepository.findById(1L).get();
// Count number of records in Area table.
long count = areaRepository.count();
if (count < 3) {
isValid = true;
}
} catch (Exception e) {
System.out.println(e);
}
return isValid;
}
}
It was expected that on first iteration variable count will be 0, but is errors have triggering count() call. Also, why would call to findById work, but not call other methods. As a result, the insertion call (save) is just being rolled back.
UPDATE - Area
#Audited
#Entity
#Counter
public class Area {
private static final long serialVersionUID = 5058628890621461831L;
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 50)
#Column(length = 50, nullable = false)
private String name;
public Area() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Why ResponseEntity calls hibernate while lazy loading active

Below is the DAO. I am getting the first UppeningUsers object. Note that here for this function I do not want to return peopleWhoBlockedMe set which is located inside the UppeningUsers..
But in different functions I would like to return that information. Note that Both of them are LAZY fetching. With evict I tried to detach the object but still it did not work.
First of all RESTcontroller is below. Then the DAO code is below. Then two entity descriptions are below.
Question is: I see that until
return new ResponseEntity(returned, HttpStatus.OK);
There is only one query which is the typical select. I do not want hibernate to go and take also UserBlock information of that specific UppeningUser. Because it is not needed for this service response. However even though it is lazy loading for some reason
return new ResponseEntity(returned, HttpStatus.OK);
calls the hibernate. I dont know why in restcontroller still it is connected to the database. I tried evict but didnt work.
The json response is
{"id":7,"peopleWhoBlockedMe":[{"blockedId":7}]}
But I do not want for this function to return this peopleWhoBlockedMe. It can be empty.
PLEASE NOTE that in other service for example I will explictly request this peopleWhoBlockedMe but just for this business logic I do not need this information. So what I can do to prevent this so whenever I actually want to call peopleWhoBlockedMe I can get it. Not automaticly.
#RestController
public class TempController {
#Autowired
UppeningUsersService uppeningUsersService;
#RequestMapping(value = "/testing", method = RequestMethod.GET)
public ResponseEntity<UppeningUsers> getPhotos() {
try {
UppeningUsers returned = uppeningUsersService.getUsersDetailsPartial();
return new ResponseEntity<UppeningUsers>(returned, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
This part is the DAO.
#Repository
public class UppeningUsersDAO {
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
/**
* Get Existing user. Return error if there is not.
* #param incomingUser user who requested access.
* #return returns the guy information. All information.
*/
#Transactional
public UppeningUsers getUserDetails() throws Exception {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("from UppeningUsers ");
UppeningUsers returning = (UppeningUsers) query.list().get(0);
session.evict(returning);
return returning;
}
}
The main table is this one..
#Entity
#Table(name = "uppening_users")
#Proxy(lazy = true)
public class UppeningUsers {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private
int id;
#OneToMany(mappedBy = "blockedId",cascade =CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserBlocks> peopleWhoBlockedMe;
public UppeningUsers() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set<UserBlocks> getPeopleWhoBlockedMe() {
return peopleWhoBlockedMe;
}
public void setPeopleWhoBlockedMe(Set<UserBlocks> peopleWhoBlockedMes) {
this.peopleWhoBlockedMe = peopleWhoBlockedMes;
}
}
Now here is the other table.
#Entity
#Table(name="user_blocks")
#Proxy(lazy = true)
public class UserBlocks {
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
int id;
#Column(name = "blocked_id",insertable = false,updatable = false)
private int blockedId;
public int getBlockedId() {
return blockedId;
}
public void setBlockedId(int blockedId) {
this.blockedId = blockedId;
}
}
UPDATE: 2 forgot to add the service
#Service("uppeningUserService")
public class UppeningUsersService {
#Autowired
UppeningUsersDAO uppeningUsersDAO;
public UppeningUsers getUsersDetailsPartial( ) throws Exception {
return uppeningUsersDAO.getUserDetails();
}
}
Jens is right about her sentence. The layer methodology and writing business objects fix the issue. Thank you.

JPA Entity found by find in a stateful ejb extended is not managed

I thought that an entity found by em.find was automatically managed by em, even out a transaction, but this class below seems to show the contrary. Was I wrong or what is the mistake in that class?
#Stateful
#TransactionAttribute(NOT_SUPPORTED)
public class CustomerGateway {
#PersistenceContext(unitName = "customersPU", type = EXTENDED)
private EntityManager em;
private Customer customer;
public Customer find(Long id) {
// customer is not managed!
this.customer = em.find(Customer.class, id);
// Print false!
System.out.println("Method find: " + em.contains(customer));
// Print false too (2 is the id of an entity)!
System.out.println("Method find: " + em.contains(em.find(Customer.class, 2L));
// A workaround
customer = em.merge(customer);
// Print true.
System.out.println("Method find after merge: " + em.contains(customer));
return this.customer;
}
EDIT 1: code of the entity
#Entity
#NamedQuery(name = "Customer.all", query = "select c from Customer c")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Customer() {
}
public Customer(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Customer)) {
return false;
}
Customer other = (Customer) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.Customer[ id=" + id + " ]";
}
}
Code of the stateful EJB:
#Stateful
#TransactionAttribute(NOT_SUPPORTED)
public class CustomerGateway {
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager em;
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void create(Customer customer) {
em.persist(customer);
this.customer = customer;
}
public Customer find(Long id) {
this.customer = em.find(Customer.class, id);
System.out.println("customer managed ? " + em.contains(this.customer));
// Workaround :
// this.customer = em.merge(customer);
return customer;
}
public void remove(Long id) {
Customer cust = em.getReference(Customer.class, id);
em.remove(cust);
}
#TransactionAttribute(REQUIRES_NEW)
public void save() {
}
public List<Customer> findAllCustomers() {
Query query = em.createNamedQuery("Customer.all");
return query.getResultList();
}
#Remove
public void close() {
}
}
I work with NetBeans 7.4, GlassFish 4.0, EJB 3.2, Java DB.
All that you have experienced is according to the spec. The persistence context remains (and the entities keeps attached) while the transaction exists. So, in a extended persistence context and a NOT_SUPPORTED transaction the objects retrieved by calling find method are dettached. -Also, if your Customer object has lazy relationships and you try to access them, then, it is highly probable that you will get a runtime exception.-
Now, why the merge method is just ok?. Well, first remember that merge returns a managed entity and is attaching the customer to the persistence context.
Second, you have an EXTENDED persistence context, so, it wont go to update the database until you call the #Remove annotated method. When this call arrives, you will probably get a TransactionRequiredException.
EDIT 1 --------------------------------------------------------------------------------
According to your comments:
find is not required to be in a transaction, although, if you want managed object there must be one.
The paragraph is about EM the life cycle (3.3 section), in this case, tries to explain that at the end of a method for a transaction-scoped bean, the entities will be detached, but, in the case of extended EM the entities will remains attached.
There are 2 insightful paragraphs :
When an EM with an extended persistence context is used, the persist, remove, merge and refresh operations can be called regardless of whether a transaction is active. The effects of these operations will be committed to the database when the extended persistence context is enlisted in a transaction and the transaction commits.
The persistence context is closed by the container when the #Remove method of the stateful session bean completes (or the stateful session bean instance is otherwise destroyed).
Looks like the method that you initially omit in the question with #TransactionAttribute(REQUIRES_NEW) is the place where the merge occurs successfully. That's why you have no exception.
EDIT 2 --------------------------------------------------------------------------------
After some testing, GF4 has a bug and has been reported > https://java.net/jira/browse/GLASSFISH-20968
EDIT 3 ---------------------------------------------------------------------------------
20/May/2014 : The bug has been marked as : Must Fix for Glassfish 4.0.1.
According to Checkus, it seems to be a bug in GF4: https://java.net/jira/browse/GLASSFISH-20968

NullPointerException thrown on flush after upgrading to Hibernate 4.1.3 Final

I have just completed an upgrade from Hibernate 3.6 to 4.1.3 Final and at first everything seemed to go fine. However, one of my colleagues recently tested this an in one scenario he gets a NullPointer being thrown from within Hibernate (and this exception was not being thrown before we upgraded for the exact same DB). It is an incredibly strange scenario. We have an entity called BlogPost that looks like the below and it extends some mapped superclasses (that I have also included):
#Entity
#Table(name = "blog_post")
public class BlogPost extends CommunityModelObject implements HasFeedPost {
#Lob
private String title;
#Lob
private String content;
#Enumerated
#Column(nullable = false)
private CBlogPost.Status status = CBlogPost.Status.UNPUBLISHED;
// Reference to the feed post that indicates that this blog post has been published
#OneToOne
#JoinColumn(name = "feed_post_id")
private FeedPost feedPost;
#ManyToOne
#JoinColumn(name = "posted_by_employee_id")
private Employee postedBy;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public CBlogPost.Status getStatus() {
return status;
}
public void setStatus(CBlogPost.Status status) {
this.status = status;
}
#Override
public FeedPost getFeedPost() {
return feedPost;
}
#Override
public void setFeedPost(FeedPost feedPost) {
this.feedPost = feedPost;
}
public Employee getPostedBy() {
return postedBy;
}
public void setPostedBy(Employee postedBy) {
this.postedBy = postedBy;
}
}
#Filter(name = "tenantFilter", condition = "(tenant_id = :tenantId or tenant_id is null)")
#MappedSuperclass
public abstract class CommunityModelObject extends ModelObject {
#IndexedEmbedded(prefix = "tenant", indexNullAs = IndexedEmbedded.DEFAULT_NULL_TOKEN)
#ManyToOne
#JoinColumn(name = "tenant_id")
protected Tenant tenant;
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
/**
* If the Tenant is null then it can be accessed / viewed by the entire "community" / user base
*/
public boolean isCommunityObject() {
return tenant == null;
}
}
#MappedSuperclass
public abstract class ModelObject extends BaseModelObject {
#Id
#GeneratedValue
private Long id;
#Override
public long getId() {
return (id == null ? 0 : id);
}
public void setId(long id) {
this.id = (id == 0 ? null : id);
}
}
#MappedSuperclass
public abstract class BaseModelObject implements java.io.Serializable {
// This annotation ensures that a column is not associated with this member (simply omitting the #Column annotation is not enough since
// that annotation is completely optional)
#Transient
private boolean doNotAutoUpdateDateUpdated = false;
#Version
protected int version;
#Column(name = "date_created")
protected Date dateCreated;
#Column(name = "date_updated")
protected Date dateUpdated;
public abstract long getId();
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public Date getDateCreated() {
return dateCreated;
}
public Date getDateUpdated() {
return dateUpdated;
}
/**
* This will set the dateUpdated to whatever is passed through and it will cause the auto update (pre-update) to NOT occur
*
* #param dateUpdated
*/
public void setDateUpdated(Date dateUpdated) {
doNotAutoUpdateDateUpdated = true;
this.dateUpdated = dateUpdated;
}
public void touch() {
// By setting date updated to null this triggers an update which results in onUpdate being called and the nett
// result is dateUpdated = new Date()
dateUpdated = null;
}
#PrePersist
protected void onCreate() {
dateCreated = new Date();
}
#PreUpdate
protected void onUpdate() {
if (!doNotAutoUpdateDateUpdated) {
dateUpdated = new Date();
}
}
#Override
public boolean equals(Object obj) {
long id = getId();
if (id == 0) {
return this == obj;
}
//Use Hibernate.getClass() because objects might be proxies
return obj != null &&
obj instanceof BaseModelObject &&
Hibernate.getClass(this) == Hibernate.getClass(obj) &&
getId() == ((BaseModelObject)obj).getId();
}
#Override
public int hashCode() {
Long id = getId();
return id == 0 ? super.hashCode() : id.intValue();
}
#Override
public String toString() {
return getClass().getSimpleName() + "-" + getId();
}
}
The strangest thing is happening when I query BlogPost in some scenarios. If I run the query below, for example, in isolation then it works fine but if I run it in amongst a bunch of other queries then I get the exception below:
select b from BlogPost b
java.lang.NullPointerException
at org.hibernate.event.internal.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:240)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:163)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:55)
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1153)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1208)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:256)
Now the kicker is that if I take all of the fields from all of the mapped superclasses that I listed above and put them directly into BlogPost and make BlogPost just extend nothing and implement java.io.Serializable then everything works perfectly. This leads me to believe that the bug is either related to mapped superclasses or the Hibernate filter that I am applying to CommunityModelObject.
Any ideas as to how to solve this? I am assuming that it is a newly introduced bug in Hibernate but I may be wrong. This is causing major issues for us since we need to upgrade asap in order to upgrade Hibernate Search which we need to do for a critical bug fix.
Also note that the DB we are using is MySQL with the following custom dialect that I wrote when doing this upgrade to handle our BIT columns:
public class MySQL5InnoDBDialectExt extends MySQL5InnoDBDialect {
private static final String BIT_STRING = "bit";
public MySQL5InnoDBDialectExt() {
super();
registerColumnType(Types.BOOLEAN, BIT_STRING);
}
}
Thanks,
Brent
I sorted this issue out, found the problem by fluke. Here is the resolution as I posted it on the Hibernate forum:
I found the issue. It does not seem to be related to interceptors,
rather to either caching or instrumentation. Basically our app
automatically includes all entities within a very specific package in
our caching scheme and the same classes in our instrumentation. We
generally have all of our entities in this package, however this one
which was causing the issue was the only one not included in this
package. The previous version of EhCache / Hibernate that we were
using seemed ok with this, but after upgrading it caused issues.
Anyway, the entity was in the incorrect package, when I refactored it
and moved it into the correct package then everything worked! So it
was not a bug in Hibernate, just an informative exception that made it
difficult to track this issue down (I basically solved it by complete
fluke).
Hope this helps somebody, but in my case it was a problem with a wrong instrumentation.
I have class 'A' and two child classes 'B' and 'C'. 'A' class has a lazy property and it is instrumented to make the lazy accessing works.
But the mistake was that I didn't instrument the child classes 'B' and 'C', therefore any access to the instrumented property from 'B' and 'C' caused the exception.
When I instrumented 'B' and 'C', the problem went away.

Categories

Resources