How to lazy fetch LOBs in Oracle Weblogic application server? - oracle

I'm running an application on a Weblogic App-Server (10.3.3) on an Oracle database (10g and later on 11g), and it appears that my application's memory isn't managed correctly, as after running it for a while it starts to repeatedly throw "gc overhead limit exceeded" exception.
I profiled my server with jvisualvm, and it appears that most of the heap consists of byte arrays, and these byte arrays are associated with one of my main entities as its 'data' member (BLOB in the DB).
I've tried to change that entity with something like this:
#Basic(fetch=LAZY)
#LOB
public byte[] getData() { return this.data; }
but then I always get null.
Is there anyway to lazy-fetch my LOBs?
Update
I'm using the default provider which comes with Weblogic application server. I didn't touch any JPA configuration on the server.
My code is very simple at the moment (sort of a sandbox), and it just finds my entity using em.find() (according to the row id) and then I just call myEntity.getData() which returns null.

It's me again, having problem logging in to the same user.
Anyway, found a solution:
I switched the JPA Provider of my server to TopLink, but then the best I could get is a non-lazy fetching (but at least no null or exception).
Then I tried a different aproach. I created a new entity for the same table which holds my lob field, while my previous entity didn't hold the lob field, but the new entity. I connected them two with a one-to-one relationship and lazy fetching, and it worked!
It took me some time because only a specific mapping works, as you can see here:
#Entity
#Table(name="MY_TABLE")
public class A implements Serializable {
private Long id;
private ALob lob;
#Id
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
#OneToOne(fetch=LAZY)
#JoinColumn(name="ID", insertable=false, updatable=false)
public ALob getLob() {return lob;}
public void setLob(ALob lob) {this.lob = lob;}
}
#Entity
#Table(name="MY_TABLE")
public class ALob implements Serializable {
private Long id;
private byte[] data;
#Id
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public byte[] getData() {return data;}
public void setData(byte[] data) {this.data = data;}
}
Oh and btw, it doesn't work with kodo, only TopLink.

Related

Update millions of database row using Spring Data JPA

I'm wondering on which is the best for this
Here is my Entity:
#Entity
#Data
public class User {
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String middleName;
private String lastName;
private Status status;
private String statusRemarks;
}
Option 1 Direct bulk update in UserRepository:
Does this will affect my database performance if the users to be update will reach to millions in number?
public interface UserRepository extends CrudRepository<User, Long> {
#Query("UPDATE u FROM User u set u.status=:status, u.statusRemarks=:statusRemarks where u.status in :statuses")
void bulkUpdateByStatuses(Status status,String statusRemarks,Status... statuses);
}
Option 2 will fetch the users by status and will update it one by one like this:
I'm pretty sure this will affect the performance of the MS due to the memory usage
public void bulkUpdateUserByStatuses(final UserBulkUpdateDto userbulkUpdateDto){
List<User> toUpdateUsers = userRepository.findByStatuses(userbulkUpdateDto.getStatuses())
for(final User user: toUpdateUsers){
user.setStatus(userbulkUpdateDto.getNewStatus());
user.setStatusRemarks(userbulkUpdateDto.getStatusRemarks());
userRepository.save(user);
}
}
}
Absolutely option 1.
Both options read the affected data, modify it and write it back.
The option 2 on top of that reads and writes the data across the network and turns it into java objects.
So it will be slower and probably even put a higher load on the database.
If the load still becomes to high for the database you should consider breaking the update in smaller ones, for example by limiting to a range of ids at a time.

Saving Entity with Cached object in it causing Detached Entity Exception

I'm trying to save an Entity in DB using Spring Data/Crud Repository(.save) that has in it another entity that was loaded through a #Cache method. In other words, I am trying to save an Ad Entity that has Attributes entities in it, and those attributes were loaded using Spring #Cache.
Because of that, I'm having a Detached Entity Passed to Persist Exception.
My question is, is there a way to save the entity still using #Cache for the Attributes?
I looked that up but couldn't find any people doing the same, specially knowing that I am using CrudRepository that has only the method .save(), that as far as I know manages Persist, Update, Merge, etc.
Any help is very much appreciated.
Thanks in advance.
Ad.java
#Entity
#DynamicInsert
#DynamicUpdate
#Table(name = "ad")
public class Ad implements SearchableAdDefinition {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private User user;
#OneToMany(mappedBy = "ad", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<AdAttribute> adAttributes;
(.....) }
AdAttribute.java
#Entity
#Table(name = "attrib_ad")
#IdClass(CompositeAdAttributePk.class)
public class AdAttribute {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ad_id")
private Ad ad;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "attrib_id")
private Attribute attribute;
#Column(name = "value", length = 75)
private String value;
public Ad getAd() {
return ad;
}
public void setAd(Ad ad) {
this.ad = ad;
}
public Attribute getAttribute() {
return attribute;
}
public void setAttribute(Attribute attribute) {
this.attribute = attribute;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
#Embeddable
class CompositeAdAttributePk implements Serializable {
private Ad ad;
private Attribute attribute;
public CompositeAdAttributePk() {
}
public CompositeAdAttributePk(Ad ad, Attribute attribute) {
this.ad = ad;
this.attribute = attribute;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CompositeAdAttributePk compositeAdAttributePk = (CompositeAdAttributePk) o;
return ad.getId().equals(compositeAdAttributePk.ad.getId()) && attribute.getId().equals(compositeAdAttributePk.attribute.getId());
}
#Override
public int hashCode() {
return Objects.hash(ad.getId(), attribute.getId());
}
}
Method using to load Attributes:
#Cacheable(value = "requiredAttributePerCategory", key = "#category.id")
public List<CategoryAttribute> findRequiredCategoryAttributesByCategory(Category category) {
return categoryAttributeRepository.findCategoryAttributesByCategoryAndAttribute_Required(category, 1);
}
Method used to create/persist the Ad:
#Transactional
public Ad create(String title, User user, Category category, AdStatus status, String description, String url, Double price, AdPriceType priceType, Integer photoCount, Double minimumBid, Integer options, Importer importer, Set<AdAttribute> adAtributes) {
//Assert.notNull(title, "Ad title must not be null");
Ad ad = adCreationService.createAd(title, user, category, status, description, url, price, priceType, photoCount, minimumBid, options, importer, adAtributes);
for (AdAttribute adAttribute : ad.getAdAttributes()) {
adAttribute.setAd(ad);
/* If I add this here, I don't face any exception, but then I don't take benefit from using cache:
Attribute attribute = attributeRepository.findById(adAttribute.getAttribute().getId()).get();
adAttribute.setAttribute(attribute);
*/
}
ad = adRepository.save(ad);
solrAdDocumentRepository.save(AdDocument.adDocumentBuilder(ad));
return ad;
}
I don't know if you still require this answer or not, since it's a long time, you asked this question. Yet i am going to leave my comments here, someone else might get help from it.
Lets assume, You called your findRequiredCategoryAttributesByCategory method, from other part of your application. Spring will first check at cache, and will find nothing. Then it will try to fetch it from Database. So it will create an hibernate session, open a transaction, fetch the data, close the transaction and session. Finally after returning from the function, it will store the result set in cache for future use.
You have to keep in mind, those values, currently in the cache, they are fetched using a hibernate session, which is now closed. So they are not related to any session, and now at detached state.
Now, you are trying to save and Ad entity. For this, spring created a new hibernate session, and Ad entity is attached to this particular session. But the attributes object, that you fetched from the Cache are detached. That's why, while you are trying to persist Ad entity, you are getting Detached Entity Exception
To resolve this issue, you need to re attach those objects to current hibernate session.I use merge() method to do so.
From hibernate documentation here https://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Session.html
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".
Simply put, this will attach your object to hibernate session.
What you should do, after calling your findRequiredCategoryAttributesByCategory method, write something like
List attributesFromCache = someService.findRequiredCategoryAttributesByCategory();
List attributesAttached = entityManager.merge( attributesFromCache );
Now set attributesAttached to your Ad object. This won't throw exception as attributes list is now part of current Hibernate session.

I need help for persisting into oracle database

There is a problem about generating id while persisting into database.
I added the following code to my jpa entity file, however I'm getting 0 for personid.
#Id
#Column(unique=true, nullable=false, precision=10, name="PERSONID")
#SequenceGenerator(name="appUsersSeq", sequenceName="SEQ_PERSON", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "appUsersSeq")
private long personid;
EjbService:
#Stateless
public class EjbService implements EjbServiceRemote {
#PersistenceContext(name = "Project1245")
private EntityManager em;
#Override
public void addTperson(Tperson tp) {
em.persist(tp);
}
}
0 is default value for long type. The id will be set after invoking select query for the related sequence, which commonly is executed when you persist the entity. Are you persisting the entity? In case yes, post the database sequence definition to check it.

OptimisticLockException not thrown when version has changed

I've created a simple EJB application that uses JPA for persistence and have a problem whereby optimistic locking is not functioning as I would have expected.
The application contains a class named Site which defines the model for a table named SITE in the database. The SITE table contains a column named ROW_VERSION which is referenced in the Site class using the #version annotation.
Whenever the record is updated, the ROW_VERSION is incremented by 1. So far, so good.
The problem arises when the row has changed in the time between the application reading the row using the EntityManager find method and the row being updated by the EntityManager merge method. As the ROW_VERSION for the row has been incremented by 1 and therefore is not the same as when the EntityManager find method was called, I would expect an OptimisticLockException to be thrown, but instead the changes are written to the table and in turn overwriting the changes made by the other process.
The application is running on WebSphere 8.5 and is using OpenJPA provided by the container.
Have I mis-understood how optimistic locking is supposed to work or is there something else that I need to do to make the OptimisticLockException occur?
The Site class is as follows:
#Entity
#Table(name="SITE")
public class Site {
#Id
#Column(name="SITE_ID")
private int id;
#Column(name="SITE_NAME")
private String siteName;
#Column(name="SITE_ADDRESS")
private String address;
#Column(name="ROW_VERSION")
#Version
private long rowVersion;
//getters and setters
}
The application makes uses of the Generic DAO wrapper class to invoke the EntityManager methods. The contents of the class are as follows:
public abstract class GenericDAO<T> {
private final static String UNIT_NAME = "Test4EJB";
#PersistenceContext(unitName = UNIT_NAME)
private EntityManager em;
private Class<T> entityClass;
public GenericDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
public T update(T entity) {
return em.merge(entity);
}
public T find(int entityID) {
return em.find(entityClass, entityID);
}
//other methods
}
Update - I've done some more investigation and have found this http://pic.dhe.ibm.com/infocenter/wasinfo/v8r0/index.jsp?topic=%2Fcom.ibm.websphere.nd.multiplatform.doc%2Finfo%2Fae%2Fae%2Fcejb_genversionID.html but even when I've added the #VersionColumn and #VersionStrategy annotations I still cannot get the OptimisticLockException to be thrown.

How to persist relationships between Neo4J NodeEntitys in Spring Data Graph without calling persist twice

The test below fails if I remove the first persist(). Why do I need to persist the NodeEntity in order for the Set to be instantiated? Is there some better way to do this? I don't want to have to write to the database more often than nessesary.
#Test
public void testCompetenceCreation() {
Competence competence = new Competence();
competence.setName("Testcompetence");
competence.persist(); //test fails if this line is removed
Competence competenceFromDb = competenceRepository.findOne(competence.getId());
assertEquals(competence.getName(), competenceFromDb.getName());
Education education = new Education();
education.setName("Bachelors Degree");
competence.addEducation(education);
competence.persist();
assertEquals(competence.getEducations(), competenceFromDb.getEducations());
}
If i remove the mentioned line, the exception bellow occurs:
Throws
java.lang.NullPointerException
at com.x.entity.Competence.addEducation(Competence.java:54)
Competence.class:
#JsonIgnoreProperties({"nodeId", "persistentState", "entityState"})
#NodeEntity
public class Competence {
#RelatedTo(type = "EDUCATION", elementClass = Education.class)
private Set<Education> educations;
public Set<Education> getEducations() {
return educations;
}
public void addEducation(Education education) {
this.educations.add(education);
}
}
Education.class
#JsonIgnoreProperties({"nodeId", "persistentState", "entityState"})
#NodeEntity
public class Education {
#GraphId
private Long id;
#JsonBackReference
#RelatedTo(type = "COMPETENCE", elementClass = Competence.class, direction = Direction.INCOMING)
private Competence competence;
#Indexed
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
What version of SDN are you running?
Because up until the first persist the entity is detached and AJ doesn't take care of the fields (like creating the managed set). Persist creates the node at connects it to the entity, from then on until the transaction commits your entity is attached and all the changes will be written through.
It only writes to the db at commit, so no worries about too many writes. All the other changes will just be held in memory for your transaction. Probably you should also annotate the test method with #Transactional.
Can you create a JIRA issue for this? So that a consistent handling is provided. (Problem being that it probably also complains when you initialize the set yourself.)
Two other things:
as your relationship between Education<--Competence is probably the same and should just be navigated in the other direction you must provide the same type name in the annotation.
e.g. Education<-[:PROVIDES]-Competence
also if you don't call persist your entity will not be created and then the findOne by returning null

Resources