Hi I have created many to one relationship in hibernate.
Following is the code for that.
there are thousands of records present in B table which is link to single record of table A. When i used getBList() method it will returns thousands of record and JAVA goes OUT OF MEMORY.
So how can i solve this problem.
#Entity
#Table(name = "A")
public class A {
private int Id;
private String aName;
private List<MksReleaseInfo> bList;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
public int getId() {
return releaseId;
}
public void setId(final int Id) {
this.Id = Id;
}
#Column(name = "aname", unique = true)
public String getAName() {
return aName;
}
public void setAName(final String aName) {
this.aName = aName;
}
#OneToMany(mappedBy = "aName")
public List<MksReleaseInfo> getBList() {
return bList;
}
public void setBList(final List<B> bList) {
this.bList = bList;
}
}
#Entity
#Table(name = "B")
public class B {
private int bIndex;
private int bpriority;
private A aName;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
protected int getBIndex() {
return mksReleaseInfoIndex;
}
protected void setBIndex(final int bIndex) {
this.bIndex = bIndex;
}
#Column(name = "priority")
public int getBPriority() {
return bpriority;
}
public void setBPriority(final int bpriority) {
this.bpriority = bpriority;
}
#ManyToOne
#JoinColumn(name = "Id")
public A getAName() {
return aName;
}
public void setAName(final A aName) {
this.aName = aName;
}
}
after all the comments i have implemented the following code. but again it gives OUT OF MEMORY. Should i have to flush the memory explicitly and how?
public List<B> getList(String name, int offset, int limit) throws DAOException {
try {
String hql = "from B where name = :name";
begin();
Query query = getSession().createQuery(hql);
query.setString("name", name);
if(offset > 0){
query.setFirstResult(offset);
}
if(limit > 0){
query.setMaxResults(limit);
query.setFetchSize(limit);
}
commit();
return query.list();
} catch (HibernateException e) {
rollback();
}
}
public Long countB(String name) throws DAOException {
try {
String hql = "select count(*) from B where name = :name";
begin();
Query query = getSession().createQuery(hql);
query.setString("name", name);
commit();
return (Long)query.uniqueResult();
} catch (HibernateException e) {
rollback();
}
}
long count = countB(name);
int counter = (int) (count / 200);
if(count%200 > 0){
counter++;
}
for(int j = 0;j<counter;j++){
lists = getList(name, j*200, 200);
for(B count1 : lists){
System.out.println(count1);
}
}
You could introduce a DAO in order to retrieve the records from B given a A object in a paged way.
For example:
public interface BDao {
Page findByA(A a, PageRequest pageRequest);
}
Maybe you could take an idea from approach taked in Spring Data
Set MaxResults property of datasource, it will set limit on number of records you are getting.
Also, you can increase java heap memory size using -Xmx256m. This will set maximum heap allocation size to 256MB. You can set it as you need.
You can use query with paging for this purpose. In Query class you can find setFirstResult and setMaxResults methods which can help you to iterate over records. If you need to load all B objects and store them you can adjust memory settings of java by setting -Xmx option. Also you can try to declare some kind of reduced class B (for example ReducedB), which contains only required fields, and use iterating with converting B to ReducedB to reduce memory usage.
Also you can check this question. I think that it is close enought to what you want.
P.S. Final solution would depend on particular issue that you want to solve.
I had the same issue. I looked at my code and server space but nothing helped. Later I looked into data and realized wrongly placed data was making application use lot of processing power. Make sure you do not have duplicated data in child class.
Related
I'm using EclipseLink to run some Native SQL. I need to return the data into a POJO. I followed the instructions at EclipseLink Docs, but I receive the error Missing descriptor for [Class]
The query columns have been named to match the member variables of the POJO. Do I need to do some additional mapping?
POJO:
public class AnnouncementRecipientsFlattenedDTO {
private BigDecimal announcementId;
private String recipientAddress;
private String type;
public AnnouncementRecipientsFlattenedDTO() {
super();
}
public AnnouncementRecipientsFlattenedDTO(BigDecimal announcementId, String recipientAddress, String type) {
super();
this.announcementId = announcementId;
this.recipientAddress = recipientAddress;
this.type = type;
}
... Getters/Setters
Entity Manager call:
public List<AnnouncementRecipientsFlattenedDTO> getNormalizedRecipientsForAnnouncement(int announcementId) {
Query query = em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT, AnnouncementRecipientsFlattenedDTO.class);
query.setParameter(1, announcementId);
return query.getResultList();
}
I found out you can put the results of a Native Query execution into a List of Arrays that hold Objects. Then one can iterate over the list and Array elements and build the desired Entity objects.
List<Object[]> rawResultList;
Query query =
em.createNamedQuery(AnnouncementDeliveryLog.FIND_NORMALIZED_RECIPIENTS_FOR_ANNOUNCEMENT);
rawResultList = query.getResultList();
for (Object[] resultElement : rawResultList) {
AnnouncementDeliveryLog adl = new AnnouncementDeliveryLog(getAnnouncementById(announcementId), (String)resultElement[1], (String)resultElement[2], "TO_SEND");
persistAnnouncementDeliveryLog(adl);
}
You can only use native SQL queries with a class if the class is mapped. You need to define the AnnouncementRecipientsFlattenedDTO class as an #Entity.
Otherwise just create the native query with only the SQL and get an array of the data back and construct your DTO yourself using the data.
Old question but may be following solution will help someone else.
Suppose you want to return a list of columns, data type and data length for a given table in Oracle. I have written below a native sample query for this:
private static final String TABLE_COLUMNS = "select utc.COLUMN_NAME, utc.DATA_TYPE, utc.DATA_LENGTH "
+ "from user_tab_columns utc "
+ "where utc.table_name = ? "
+ "order by utc.column_name asc";
Now the requirement is to construct a list of POJO from the result of above query.
Define TableColumn entity class as below:
#Entity
public class TableColumn implements Serializable {
#Id
#Column(name = "COLUMN_NAME")
private String columnName;
#Column(name = "DATA_TYPE")
private String dataType;
#Column(name = "DATA_LENGTH")
private int dataLength;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public TableColumn(String columnName, String dataType, int dataLength) {
this.columnName = columnName;
this.dataType = dataType;
this.dataLength = dataLength;
}
public TableColumn(String columnName) {
this.columnName = columnName;
}
public TableColumn() {
}
#Override
public int hashCode() {
int hash = 0;
hash += (columnName != null ? columnName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof TableColumn)) {
return false;
}
TableColumn other = (TableColumn) object;
if ((this.columnName == null && other.columnName != null) || (this.columnName != null && !this.columnName.equals(other.columnName))) {
return false;
}
return true;
}
#Override
public String toString() {
return getColumnName();
}
}
Now we are ready to construct a list of POJO. Use the sample code below to construct get your result as List of POJOs.
public List<TableColumn> findTableColumns(String table) {
List<TableColumn> listTables = new ArrayList<>();
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery(TABLE_COLUMNS, TableColumn.class).setParameter(1, table);
listTables = q.getResultList();
em.close();
return listTables;
}
Also, don't forget to add in your POJO class in persistence.xml! It can be easy to overlook if you are used to your IDE managing that file for you.
Had the same kind of problem where I wanted to return a List of POJOs, and really just POJOs (call it DTO if you want) and not #Entity annotated Objects.
class PojoExample {
String name;
#Enumerated(EnumType.STRING)
SomeEnum type;
public PojoExample(String name, SomeEnum type) {
this.name = name;
this.type = type;
}
}
With the following Query:
String query = "SELECT b.name, a.newtype as type FROM tablea a, tableb b where a.tableb_id = b_id";
Query query = getEntityManager().createNativeQuery(query, "PojoExample");
#SuppressWarnings("unchecked")
List<PojoExample> data = query.getResultList();
Creates the PojoExample from the database without the need for an Entity annotation on PojoExample. You can find the method call in the Oracle Docs here.
edit:
As it turns out you have to use #SqlResultSetMapping for this to work, otherwise your query.getResultList() returns a List of Object.
#SqlResultSetMapping(name = "PojoExample",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "type", type = String.class)
},
targetClass = PojoExample.class)
)
Just put this anywhere under your #Entity annotation (so in this example either in tablea or tableb because PojoExample has no #Entity annotation)
This is maybe a beginner question on hibernate. I am doing my first steps, I designed a simple datamodel consisting of about 10 entities and I use hibernate to persist them to my Oracle XE database. Now I am facing the following problem: First time, when I do a transaction to persist some entities, they are persisted properly. I verify, that the data exists in the database and then I delete all the entries from all database tables. I verify that all tables are empty again. Then I run my program again to persist some new entities - and here happens something really strange: Afterwards I find in my databse the new entries as well as the old ones, which were persisted last time and which I had deleted! They contained the old IDs and the old data fields! How can this be? This happens even if I shut down my computer after the first time the program runs! How does it remember the old entries and where are they saved? Do you have any ideas?
Some information, that might be useful:
I am using annotations (instead of config files) for the mapping.
Following you see the classes used for persisting as well as one example of an entity (I am showing only one entity to avoid making the question too long).
As you see, I am using FetchType = EAGER on my MANY to MANY mappings (as I understand, this makes sure, that all related entities are loaded immediately together with any loaded entity). Can this have any impact?
Thanks for any help!
public class PersistenceManager {
private static final SessionFactory factory = new Configuration().configure().buildSessionFactory();
public static void sampleData() {
try(Session session = factory.openSession()) {
SampleDataLoader.loadSampleData(session);
} catch(HibernateException e) {
System.out.println("Exception during persisting! Message: " + e.getMessage());
e.printStackTrace();
}
}
}
public class SampleDataLoader {
static void loadSampleData(Session session) {
Language french = new Language("French");
Language german = new Language("German");
Noun garcon = new Noun(french, "garcon", false);
Noun junge = new Noun(german, "Junge", false);
junge.addTranslation(garcon);
ZUser user = new ZUser("Daniel", "password");
user.setOwnLanguage(german);
user.setEmail("abc#somemail.de");
user.setDateRegistered(LocalDateTime.now());
user.addForeignLanguage(french);
Transaction transaction = session.beginTransaction();
session.save(user);
session.save(french);
session.save(german);
session.save(junge);
transaction.commit();
}
}
#Entity
public class ZUser {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column
private String name;
#Column
private String password;
#Column
private String email;
#Column
private String picturePath;
#Column
private LocalDateTime dateRegistered;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="OWNLANGUAGE_ID")
private Language ownLanguage;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(name="USER_LANGUAGE",
joinColumns=#JoinColumn(name="USER_ID"),
inverseJoinColumns=#JoinColumn(name="LANGUAGE_ID")
)
private Set<Language> foreignLanguages = new HashSet<>();
public ZUser() { }
public ZUser(String n, String p) {
name = n;
password = p;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPicturePath() { return picturePath; }
public void setPicturePath(String picturePath) { this.picturePath = picturePath; }
public LocalDateTime getDateRegistered() { return dateRegistered; }
public void setDateRegistered(LocalDateTime dateRegistered) { this.dateRegistered = dateRegistered; }
public Language getOwnLanguage() { return ownLanguage; }
public void setOwnLanguage(Language ownLanguage) { this.ownLanguage = ownLanguage; }
public void addForeignLanguage(Language language) {foreignLanguages.add(language);}
public Set<Language> getForeignLanguages() {return Collections.unmodifiableSet(foreignLanguages); }
}
Clarified by the comment of Jagger (see comments). Indeed, I was using Oracle SQL command line to delete the entries and I had rgotten, that I need to explicitely commit after deleting. The solution can be so easy :)
I have problem, and I don't know how to solve it.
I have entity:
#Entity
#Table(name = "entity_languagetree")
#AttributeOverride(name = "id", column = #Column(name = "languagetree_id"))
public class LanguageTree extends BaseObject {
#ElementCollection(targetClass = java.lang.String.class, fetch = FetchType.EAGER)
#CollectionTable(name = "view_languagetree_to_stringlist")
private List<String> relationship = new ArrayList<>();
public LanguageTree() {
//
}
public List<String> getRelationship() {
return relationship;
}
public void setRelationship(List<String> relationship) {
this.relationship = relationship;
}
}
where BaseObject is
#MappedSuperclass
public class BaseObject {
#Id
#GeneratedValue
#Column(name = "entity_id")
private Long id;
/**
*
* #return true if the entity hasn't been persisted yet
*/
#Transient
public boolean isNew() {
return id == null;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Bean getBean() {
return null;
}
}
Work with object - in my servlet, I am calling jsVarTree() like this:
String var = jsVarTree();
My problem is, that after method jsVarTree is finished, hibernate delete my relationship list from entity LanguageTree. I don't know why! I am not calling any delete and etc.. (I AM SURE, I SPENT A LOT OF TIME IN DEBUGER!)
:
#Override
public String jsVarTree() {
TreeBuilder tb = new TreeBuilder(getLanguageList());
return tb.getJsVarString(); // THIS METHOD IS ONLY GETTER !!!!
}
#Override
public List<String> getLanguageList() {
LanguageTree lt = getLanguageTreeObject();
return lt.getRelationship();
}
#Override
public LanguageTree getLanguageTreeObject() {
long fakingId = languageTreeDao.getLastId();
ServerLogger.logDebug("LAST FAKING ID: " +fakingId);
return languageTreeDao.findOne(fakingId);
}
I found this log in loggor:
HibernateLog --> 15:01:03 DEBUG org.hibernate.SQL - delete from
view_languagetree_to_stringlist where LanguageTree_languagetree_id=?
Can somebody tell me, why hibernate call delete over my table?
I saw a table in phpmyadmin..
TABLE IS FULL.
String var = jsVarTree();
TABLE IS EMPTY.
Table is deleted after return tb.getJsVarString(); is finished.
Thank you for any help!
I want a JPA/Hibernate (preferably JPA) annotation that can generate the value of a column, that is not a primary key and it doesn't start from 1.
From what I have seen JPA cannot do that with #GeneratedValue and #SequenceGenerator and #TableGenerator. Or with anything else.
I have seen a solution with an extra table, which I find is not elegant.
I can live with a Hibernate annotation, because I already have hibernate annotations.
I want to use #Generated but I cannot make it work and people claim that it is possible.
#Generated(GenerationTime.INSERT)
private long invoiceNumber;//invoice number
Update: an extra requirement, if the transaction is rolled back, we can't have a gap in the numbering.
Anyone?
The #GeneratedValue only works for identifiers and so you can't use it. If you use MySQL, you are quite limited, since database sequences are not supported.
InnoDB doesn't support multiple AUTO_INCREMENT columns and if your table PK is AUTO_INCREMENTED, then you have two options:
Go for a separate table that behaves like a sequence generator, the solution you already said you are not happy about.
Use an INSERT TRIGGER to increment that column.
Here's what worked for me - we coded all of it in the service.
Here's the entity:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Registrant extends AbstractEntity {
//....
private long invoiceNumber;//invoice number
#Entity
public static class InvoiceNumberGenerator {
#Id
#GeneratedValue
private int id;
private long counter;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getCounter() {
return counter;
}
public void setCounter(long counter) {
this.counter = counter;
}
}
}
And then we have a service that does the magic (actually there's no magic, all is done manually):
public synchronized Registrant save(Registrant registrant) {
long counter = getInvoiceNumber();
registrant.setInvoiceNumber(counter);
return registrantRepository.save(registrant);
}
private long getInvoiceNumber() {
//mist: get the invoice number from the other table
long count = registrantInvoiceNumberGeneratorRepository.count();
if(count > 1) {
throw new RuntimeException(": InvoiceNumberGenerator table has more than one row. Fix that");
}
Registrant.InvoiceNumberGenerator generator;
if(count == 0) {
generator = new Registrant.InvoiceNumberGenerator();
generator.setCounter(1000001);
generator = registrantInvoiceNumberGeneratorRepository.save(generator);
} else {
generator = registrantInvoiceNumberGeneratorRepository.findFirstByOrderByIdAsc();
}
long counter = generator.getCounter();
generator.setCounter(counter+1);
registrantInvoiceNumberGeneratorRepository.save(generator);
return counter;
}
Note the synchronized method - so that nobody can get the same number.
I can't believe there's nothing automatic that can do that.
Related to #Vlad Mihalcea, now you can use #GeneratorType to generate your own custom value for non id column. For example:
Entity:
import org.hibernate.annotations.GeneratorType
#GeneratorType(type = CustomGenerator.class, when = GenerationTime.INSERT)
#Column(name = "CUSTOM_COLUMN", unique = true, nullable = false, updatable = false, lenght = 64)
private String custom;
ValueGenerator implementation:
public class CustomGenerator extends ValueGenerator<String> {
private static final String TODAY_EXAMPLE_QUERY = "from Example where createDate>:start and createDate<:end order by createDate desc";
private static final String START_PARAMETER = "start";
private static final String END_PARAMETER = "end";
private static final String NEXTVAL_QUERY = "select EXAMPLE_SEQ.nextval from dual";
private final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd");
#Override
public String generateValue(Session session, Object owner) {
Date now = new Date();
Query<Example> todayQuery = session.createQuery(TODAY_EXAMPLE_QUERY, Example.class);
query.setParameter(START_PARAMETER, start(now));
query.setParameter(END_PARAMETER, end(now));
Example lastExample = todayQuery.setMaxResult(1).setHibernateFlushMode(COMMIT).uniqueResult();
NativeQuery nextvalQuery = session.createSQLQuery(NEXTVAL_QUERY);
Number nextvalValue = nextvalQuery.setFlushMode(COMMIT).uniqueResult();
return dataFormat.format(now) + someParameter(lastExample) + nextvalValue.longValue();
}
}
Im using the entity manager JPA with eclipse link 2.3 &Derby db and I have model with 10 entities and for each entity I need to store 1000 records ,this process takes about 70 sec. I have test it for the same model with 10 entities but with 100 records the all process with the commit take about 1.2 sec which is great.
the bottle neck is the entityManager.getTransaction().commit(); which I do just one time after i persist all the data, the commit take more 95% from the all process.
when I use the JVM monitor I dive in to the commit and I see that one of the class is responsible for almost all commit time ,the class is org.eclipse.persistence.mappings.ManyToManyMapping
http://www.eclipse.org/eclipselink/api/1.0/org/eclipse/persistence/mappings/ManyToManyMapping.html
my entities and the model doesn't have any many to many relationship or use any many to many annotation what could be the reason for the Exponential behavior ?
I have noticed that when I remove this two entities the time was saved by 85%
what is wrong with this entities
The navigation is from person which have cardinality 1 to title award that have cardinality of N i.e one person can have many awards...
#javax.persistence.Entity
#javax.persistence.Table(name = "a3_Person")
public class Person {
#javax.persistence.Id
#javax.persistence.Column(length = 20)
//// Id;
private String person_id;
public String getPerson_id() { return this.person_id; }
public void setPerson_id(String person_id) { this.person_id = person_id; }
#javax.persistence.Column(length = 30)
//// Name;
private String person_name;
public String getPerson_name() { return this.person_name; }
public void setPerson_name(String person_name) { this.person_name = person_name; }
//// Awards;
private List<TitleAward> person_awards;
public List<TitleAward> getPerson_awards() { return this.person_awards; }
public void setPerson_awards(List<TitleAward> person_awards) { this.person_awards = person_awards; }
}
#javax.persistence.Entity
#javax.persistence.Table(name = "a3_TitleAward")
public class TitleAward {
#javax.persistence.Id
#javax.persistence.Column(length = 20)
//// Id;
private String titleaward_id;
public String getTitleaward_id() { return this.titleaward_id; }
public void setTitleaward_id(String titleaward_id) { this.titleaward_id = titleaward_id; }
#javax.persistence.Column(length = 30)
//// Type;
private String titleaward_type;
public String getTitleaward_type() { return this.titleaward_type; }
public void setTitleaward_type(String titleaward_type) { this.titleaward_type = titleaward_type; }
#javax.persistence.Column(length = 30)
//// Category;
private String Cateeheihbadc;
public String getCateeheihbadc() { return this.Cateeheihbadc; }
public void setCateeheihbadc(String Cateeheihbadc) { this.Cateeheihbadc = Cateeheihbadc; }
#javax.persistence.Column()
//// Year;
private String titleaward_year;
public String getTitleaward_year() { return this.titleaward_year; }
public void setTitleaward_year(String titleaward_year) { this.titleaward_year = titleaward_year; }
#javax.persistence.Column()
//// Won;
private Boolean titleaward_won;
public Boolean getTitleaward_won() { return this.titleaward_won; }
public void setTitleaward_won(Boolean titleaward_won) { this.titleaward_won = titleaward_won; }
//// Person;
private Person Pers_fhfgdcjef;
public Person getPers_fhfgdcjef() { return this.Pers_fhfgdcjef; }
public void setPers_fhfgdcjef(Person Pers_fhfgdcjef) { this.Pers_fhfgdcjef = Pers_fhfgdcjef; }
}
There are a number of performance optimization outlined here,
http://java-persistence-performance.blogspot.com/2011/06/how-to-improve-jpa-performance-by-1825.html
ManyToManyMapping is also used for the #OneToMany annotation that uses a #JoinTable, are you using this? In general correctly profiling and understanding a profile can be difficult, so your profile may not be valid.
Please include your code, and a sample of the SQL log, and profile. You can also enable the EclipseLink PerformanceMonitor,
see,
http://www.eclipse.org/eclipselink/documentation/2.4/concepts/monitoring003.htm
If 100 records only takes 1.2s then you could probably break your process into batches of 100 and get 12s instead of 70s. 70s sounds like you have some sort of n^2 issue going on.