I have this object:
Entity
#Entity
public class someClass{
private String name;
private String labelKey;
#ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, fetch = FetchType.LAZY)
private Set<Product> products = new HashSet<>();
}
DTO
public class someClass{
private String name;
private String labelKey;
private Set<Product> products = new HashSet<>();
}
My problem is that when I get this object but products are lazy initialized, when I mapp entity to DTO using Dozer, I get a LaziInitializedException, then i want to get that when I get products lazy initialized, this products will return a empry Set.
Is this possible?
Thanks for your time and sorry for my english, it's not my native language.
As you can see in this tutorial here you can instruct dozer to exclude some field from the mapping.
If you do so, then the dozer will not invoke the method of getProducts of your entity class and therefore the exception LaziInitializedException will not be thrown.
At the same time because your DTO object is initialized with an empty HashSet for the field products, this is what will remain at the end in the DTO.
So your requirement will work, where your entity is lazily initialized for products and your DTO returns an empty list while at the same time the mapping happens from dozer.
Here is the configuration that you need for the mapper of dozer.
BeanMappingBuilder mappingExclusion = new BeanMappingBuilder() {
#Override
protected void configure() {
mapping(SomeClassEntity.class, SomeClassDto.class).exclude("products");
}
};
mapper = new DozerBeanMapper();
mapper.addMapping(mappingExclusion);
Then you can use it to do the mapping as following
mapper.map(someClassEntityInstance, someClassDtoInstance);
You could create/modify your Getter such that:
public Set<Product> getProducts() {
if (products == null) {
return new HashSet<>();
//or products = new HashSet<>(), but I'm not sure of the side effects as far as database framework is concerned.
}
return products;
}
Try marking your service class or method as #Transactional to let Spring handle session management.
public class ServiceUsingSomeClass {
final SomeClassRepository someClassRepository;
//Constructor ...
#Transactional
showProducts() {
someClassRepository.findAll();
// Do something with Set<Product>
}
}
If you only want to avoid fetching the association in cases where you use Dozer for DTO mapping, you could configure it to ignore products field in source object by extending DozerConverter and using that custom converter.
I also feel that maybe that means your target type doesn't really need to have
a products field to begin with, since you're not going to populate it.
If there's many places like this in your codebase, consider using projections to only fetch the properties necessary for the purpose at hand.
#fella7ena brings up a point about #Transactional, however this is actually unrelated - you can still come across LazyInitializationException within a transaction. This happens because Hibernate loses track of the relation between the java bean's persistence state and the database state. If you actually wanted to fetch products association from the database, you would have to use eager fetchtype (leads to n+1 issue), batching, or entitygraphs.
I'm new to using JPA and trying to transition my code from JdbcTemplate to JPA. Originally I updated a subset of my columns by taking in a map of the columns with their values and created the SQL Update string myself and executed it using a DAO. I was wondering what would be the best way to do something similar using JPA?
EDIT:
How would I transform this code from my DAO to something equivalent in JPA?
public void updateFields(String userId, Map<String, String> fields) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String> entry : fields.entrySet()) {
sb.append(entry.getKey());
sb.append("='");
sb.append(StringEscapeUtils.escapeEcmaScript(entry.getValue()));
sb.append("', ");
}
String str = sb.toString();
if (str.length() > 2) {
str = str.substring(0, str.length() - 2); // remove ", "
String sql = "UPDATE users_table SET " + str + " WHERE user_id=?";
jdbcTemplate.update(sql, new Object[] { userId },
new int[] { Types.VARCHAR });
}
}
You have to read more about JPA for sure :)
Once entity is in Persistence Context it is tracked by JPA provider till the end of persistence context life or until EntityManager#detach() method is called. When transaction finishes (commit) - the state of managed entities in persistence context is synchronized with database and all changes are made.
If your entity is new, you can simply put it in the persistece context by invoking EntityManager#persist() method.
In your case (update of existing entity), you have to get a row from database and somehow change it to entity. It can be done in many ways, but the simpliest is to call EntityManager#find() method which will return managed entity. Returned object will be also put to current persistence context, so if there is an active transaction, you can change whatever property you like (not the primary key) and just finish transaction by invoking commit (or if this is container managed transaction just finish method).
update
After your comment I can see your point. I think you should redesign your app to fit JPA standards and capabilities. Anyway - if you already have a map of pairs <Attribute_name, Attrbute_value>, you can make use of something called Metamodel. Simple usage is shown below. This is naive implementation and works good only with basic attributes, you should take care of relationships etc. (access to more informations about attributes can be done via methods attr.getJavaType() or attr.getPersistentAttributeType())
Metamodel meta = entityManager.getMetamodel();
EntityType<User> user_ = meta.entity(User.class);
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaUpdate<User> update = cb.createCriteriaUpdate(User.class);
Root e = update.from(User.class);
for( Attribute<? super User, ?> attr : user_.getAttributes() ) {
if (map.containsKey(attr.getName())) {
update.set(attr, map.get(attr));
}
}
update.where(cb.equal(e.get("id"), idOfUser));
entityManager.createQuery(update).executeUpdate();
Please note that Update Criteria Queries are available in JPA since 2.1 version.
Here you can find more informations about metamodel generation.
Alternatively to metamodel you can just use java reflection mechanisms.
JPA handles the update. Retrieve a dataset as entity using the entitymanager, change the value and call persist. This will store the changed data in your db.
In case you are using Hibernate(as JPA provider), here's an example
Entity
#Entity
#Table(name="PERSON")
public class Person {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name="NAME", nullable=false)
private String name;
other fields....
}
DAO
public interface PersonDao {
Person findById(int id);
void persist(Person person);
...
}
DaoImpl
#Repository("personDao")
public class PersonDaoImpl extends AnAbstractClassWithSessionFactory implements PersonDao {
public Person findById(int id) {
return (Person) getSession().get(Person.class, id);
}
public void persist(Person person){
getSession().persist(person);
}
}
Service
#Service("personService")
#Transactional
public class PersonServiceImpl implements PersonService {
#Autowired
PersonDao personDao;
#Override
public void createAndPersist(SomeSourceObject object) {
//create Person object and populates with the source object
Person person = new Person();
person.name = object.name;
...
personDao.persist(person);
}
#Override
public Person findById(int id) {
return personDao.findById(id);
}
public void doSomethingWithPerson(Person person) {
person.setName(person.getName()+" HELLO ");
//here since we are in transaction, no need to explicitly call update/merge
//it will be updated in db as soon as the methods completed successfully
//OR
//changes will be undone if transaction failed/rolledback
}
}
JPA documentation are indeed good resource for details.
From design point of view, if you have web interfacing, i tends to say include one more service delegate layer(PersonDelegateService e.g.) which maps the actual data received from UI to person entity (and viceversa, for display, to populate the view object from person entity) and delegate to service for actual person entity processing.
I have run into a nasty bug with jpa and hibernate. I have a billing class with the following annotation:
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="ch1_id", referencedColumnName="id")
private List<BillingItem>billingItems = new ArrayList<BillingItem>();
Now I need to filter deleted items from the collection but cannot use anything but jpa. No hibernate specific annotations allowed. So I wrote a post load function:
#PostLoad
public void postLoad() {
ArrayList<BillingItem>tempItems = new ArrayList<BillingItem>();
Iterator<BillingItem> i = this.billingItems.iterator();
BillingItem item;
while(i.hasNext()) {
item = i.next();
if( item.getStatus().equals("D")) {
tempItems.add(item);
}
}
this.billingItems.removeAll(tempItems);
}
However when there are deleted items to filter I'm seeing
Hibernate: update billing_on_item set ch1_id=null where ch1_id=? and id=?
which produces an exception because ch1_id is a foreign key and cannot be null. However hibernate is binding the parameters to correct values. Why is this update occurring in the first place? How do I correct the error?
Thanks in advance,
Randy
By removing the items from the collection, you're telling Hibernate that the association between the two entities doesn't exist anymore, so obviously, Hibernate removes what materializes this association in the database: it sets the foreign key to null.
What you probably want is just a getter in your entity that returns only the non-deleted items:
public List<BillingItem> getNonDeletedItems() {
List<BillingItem> result = new ArrayList<BillingItem>();
for (BillingItem item : this.billingItems) {
if (!"D".equals(item.getStatus()) {
result.add(item);
}
}
return result;
}
The #OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) line says that it will cascade ALL updates. Look into CascadeType.
I have made an application that displays a lot of questions from my database. For this I have made a question entity. I want to be able to "report" a question for being poor/good and so on, so for this I made a feedback entity.
The relationship between these would be: one question may have many feedbacks, and one feedback belongs to one question.
The problem is that when I save the question feedback instance it all maps perfectly in the database, but when I open a question and loops through all the feedbacks none of the feedbacks added is displayed. In order to have them displayed I need to re-deploy the web application.
Why does this happen?
For readability I only show the parts involved
QuestionFeedback entity
public class QuestionFeedback implements Serializable {
#ManyToOne
private Question question;
....
public void setQuestion(Question question) {
this.question = question;
if (!question.getFeedbacks().contains(this)) {
question.getFeedbacks().add(this);
}
}
....
}
Question entity
#Entity
public class Question implements Serializable {
#OneToMany(mappedBy = "question", fetch = FetchType.EAGER)
private List<QuestionFeedback> feedbacks;
public Question() {
feedbacks = new ArrayList<QuestionFeedback>();
}
public void addFeedback(QuestionFeedback questionFeedback) {
if (!getFeedbacks().contains(questionFeedback)) {
getFeedbacks().add(questionFeedback);
}
if (questionFeedback.getQuestion() != this) {
questionFeedback.setQuestion(this);
}
}
}
Backing bean for the report page
The question entity is already retrieved from the database.
public String flag() {
questionFeedback.setQuestion(question);
questionFeedbackService.persist(questionFeedback);
return "index";
}
DAO class
public void persist(QuestionFeedback questionFeedback) {
entityManager.persist(questionFeedback);
}
This is a simple instance of having a dirty session.
Although these can be caused by all sorts of issues, there are usually 2 simple things to keep in mind that will make it very easy to track this bug down .
First you must always remember that, when we persist our data in JPA/hibernate , we don't necessarily have any gaurantee that the transaction has completed in the database. The true meaning of the "persist" method is a common source of errors and questions, make sure you fully understand it and how it relates to your business logic. :
What's the advantage of persist() vs save() in Hibernate?
Second, after you have gauranteed that the transaction has been completed and data has been saved, you can use the EntityManager.refresh method to update the state of any objects from the database.
You can clear the JPA cache through the following code:
em.getEntityManagerFactory().getCache().evictAll();
For the record, I always flush after persisting data. Even though your database has the data, I would just try this.
public String flag() {
questionFeedback.setQuestion(question);
questionFeedbackService.persist(questionFeedback);
questionFeedbackService.flush();
return "index";
}
What is the smartest way to get an entity with a field of type List persisted?
Command.java
package persistlistofstring;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Persistence;
#Entity
public class Command implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Basic
List<String> arguments = new ArrayList<String>();
public static void main(String[] args) {
Command command = new Command();
EntityManager em = Persistence
.createEntityManagerFactory("pu")
.createEntityManager();
em.getTransaction().begin();
em.persist(command);
em.getTransaction().commit();
em.close();
System.out.println("Persisted with id=" + command.id);
}
}
This code produces:
> Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named pu: Provider named oracle.toplink.essentials.PersistenceProvider threw unexpected exception at create EntityManagerFactory:
> oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Local Exception Stack:
> Exception [TOPLINK-30005] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader#11b86e7
> Internal Exception: javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
> Exception Description: predeploy for PersistenceUnit [pu] failed.
> Internal Exception: Exception [TOPLINK-7155] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ValidationException
> Exception Description: The type [interface java.util.List] for the attribute [arguments] on the entity class [class persistlistofstring.Command] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface.
> at oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:143)
> at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createEntityManagerFactory(EntityManagerFactoryProvider.java:169)
> at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:110)
> at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
> at persistlistofstring.Command.main(Command.java:30)
> Caused by:
> ...
Use some JPA 2 implementation: it adds a #ElementCollection annotation, similar to the Hibernate one, that does exactly what you need. There's one example here.
Edit
As mentioned in the comments below, the correct JPA 2 implementation is
javax.persistence.ElementCollection
#ElementCollection
Map<Key, Value> collection;
See: http://docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html
Should anyone be looking for an alternative solution where you store your string lists as one field in your database, here's how I solved that. Create a Converter like this:
import java.util.Arrays;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import static java.util.Collections.*;
#Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
private static final String SPLIT_CHAR = ";";
#Override
public String convertToDatabaseColumn(List<String> stringList) {
return stringList != null ? String.join(SPLIT_CHAR, stringList) : "";
}
#Override
public List<String> convertToEntityAttribute(String string) {
return string != null ? Arrays.asList(string.split(SPLIT_CHAR)) : emptyList();
}
}
Now use it on your Entities like this:
#Convert(converter = StringListConverter.class)
private List<String> yourList;
In the database, your list will be stored as foo;bar;foobar, and in your Java object you will get a list with those strings.
It seems none of the answers explored the most important settings for an #ElementCollection mapping.
When you map a list with this annotation and let JPA/Hibernate auto-generate the tables, columns, etc., it'll use auto-generated names as well.
So, let's analyze a basic example:
#Entity
#Table(name = "sample")
public class MySample {
#Id
#GeneratedValue
private Long id;
#ElementCollection // 1
#CollectionTable(name = "my_list", joinColumns = #JoinColumn(name = "id")) // 2
#Column(name = "list") // 3
private List<String> list;
}
The basic #ElementCollection annotation (where you can define the known fetch and targetClass preferences)
The #CollectionTable annotation is very useful when it comes to giving a name to the table that'll be generated, as well as definitions like joinColumns, foreignKey's, indexes, uniqueConstraints, etc.
#Column is important to define the name of the column that'll store the varchar value of the list.
The generated DDL would be:
-- table sample
CREATE TABLE sample (
id bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
-- table my_list
CREATE TABLE IF NOT EXISTS my_list (
id bigint(20) NOT NULL,
list varchar(255) DEFAULT NULL,
FOREIGN KEY (id) REFERENCES sample (id)
);
This answer was made pre-JPA2 implementations, if you're using JPA2, see the ElementCollection answer above:
Lists of objects inside a model object are generally considered "OneToMany" relationships with another object. However, a String is not (by itself) an allowable client of a One-to-Many relationship, as it doesn't have an ID.
So, you should convert your list of Strings to a list of Argument-class JPA objects containing an ID and a String. You could potentially use the String as the ID, which would save a little space in your table both from removing the ID field and by consolidating rows where the Strings are equal, but you would lose the ability to order the arguments back into their original order (as you didn't store any ordering information).
Alternatively, you could convert your list to #Transient and add another field (argStorage) to your class that is either a VARCHAR() or a CLOB. You'll then need to add 3 functions: 2 of them are the same and should convert your list of Strings into a single String (in argStorage) delimited in a fashion that you can easily separate them. Annotate these two functions (that each do the same thing) with #PrePersist and #PreUpdate. Finally, add the third function that splits the argStorage into the list of Strings again and annotate it #PostLoad. This will keep your CLOB updated with the strings whenever you go to store the Command, and keep the argStorage field updated before you store it to the DB.
I still suggest doing the first case. It's good practice for real relationships later.
We can also use this.
#Column(name="arguments")
#ElementCollection(targetClass=String.class)
private List<String> arguments;
According to Java Persistence with Hibernate
mapping collections of value types with annotations [...]. At the time of writing it isn't part of the Java Persistence standard
If you were using Hibernate, you could do something like:
#CollectionOfElements(targetElement = String.class)
#JoinTable(name = "foo", joinColumns = #JoinColumn(name = "foo_id"))
#IndexColumn(name = "POSITION", base = 1)
#Column(name = "baz", nullable = false)
private List<String> arguments = new ArrayList<String>();
Update: Note, this is now available in JPA2.
When using the Hibernate implementation of JPA , I've found that simply declaring the type as an ArrayList instead of List allows hibernate to store the list of data.
Clearly this has a number of disadvantages compared to creating a list of Entity objects. No lazy loading, no ability to reference the entities in the list from other objects, perhaps more difficulty in constructing database queries. However when you are dealing with lists of fairly primitive types that you will always want to eagerly fetch along with the entity, then this approach seems fine to me.
#Entity
public class Command implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
ArrayList<String> arguments = new ArrayList<String>();
}
I had the same problem so I invested the possible solution given but at the end I decided to implement my ';' separated list of String.
so I have
// a ; separated list of arguments
String arguments;
public List<String> getArguments() {
return Arrays.asList(arguments.split(";"));
}
This way the list is easily readable/editable in the database table;
Ok i know its bit late. But for those brave souls that will see this as time passes.
As written in documentation:
#Basic:
The simplest type of mapping to a database column. The Basic annotation can be applied to a persistent property or instance variable of any of the following types: Java primitive types, [...], enums, and any other type that implements java.io.Serializable.
The important part is type that implements Serializable
So by far the most simple and easiest to use solution is simply using ArrayList instead of List (or any serializable container):
#Basic
ArrayList<Color> lovedColors;
#Basic
ArrayList<String> catNames;
Remember however that this will use system serialization, so it will come with some price, such as:
if serialized object model will change, u might not be able to restore data
small overhead is added for each element stored.
In short
it is quite simple to store flags or few elements, but i would not
recomend it to store data that might grow big.
Here is the solution for storing a Set using #Converter and StringTokenizer. A bit more checks against #jonck-van-der-kogel solution.
In your Entity class:
#Convert(converter = StringSetConverter.class)
#Column
private Set<String> washSaleTickers;
StringSetConverter:
package com.model.domain.converters;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
#Converter
public class StringSetConverter implements AttributeConverter<Set<String>, String> {
private final String GROUP_DELIMITER = "=IWILLNEVERHAPPEN=";
#Override
public String convertToDatabaseColumn(Set<String> stringList) {
if (stringList == null) {
return new String();
}
return String.join(GROUP_DELIMITER, stringList);
}
#Override
public Set<String> convertToEntityAttribute(String string) {
Set<String> resultingSet = new HashSet<>();
StringTokenizer st = new StringTokenizer(string, GROUP_DELIMITER);
while (st.hasMoreTokens())
resultingSet.add(st.nextToken());
return resultingSet;
}
}
Thiago answer is correct, adding sample more specific to question, #ElementCollection will create new table in your database, but without mapping two tables, It means that the collection is not a collection of entities, but a collection of simple types (Strings, etc.) or a collection of embeddable elements (class annotated with #Embeddable).
Here is the sample to persist list of String
#ElementCollection
private Collection<String> options = new ArrayList<String>();
Here is the sample to persist list of Custom object
#Embedded
#ElementCollection
private Collection<Car> carList = new ArrayList<Car>();
For this case we need to make class Embeddable
#Embeddable
public class Car {
}
As my reputation is not enough yet to comment on the much underrated answer written by #razvang:
As this question was asked over a decade ago, keep in mind much of the world has changed in the time since. We now have databases with native JSON column support and can use this functionality instead of using separate entities, joins or custom String-to-List converters, which are used by the other answers.
Let me suggest two purely optional changes to #razvang's superb answer though, which might be interesting depending on your specific situation:
You could omit the auto_apply = true and add #Convert(converter = <CONVERTER_CLASS_NAME>.class) to the entity field to keep control over when your converter is used.
Instead of throwing a RuntimeException whenever a conversion fails, you could handle the error right there (for example pass an empty list and write a log message) to make it fail somewhat gracefully.
What I wanted was a simple way of persisting a set of Strings, in a table column.
I ended up using JSON, as MySQL 5.7+, has native support.
Here's my solution
#Column(name = "eligible_approvers", columnDefinition = "json")
#Convert(converter = ArrayJsonConverter.class)
private Set<String> eligibleApprovers;
And then write a very basic converter
#Converter(autoApply = true)
public class ArrayJsonConverter implements AttributeConverter<Set, String> {
static final ObjectMapper mapper = new ObjectMapper();
#Override
public String convertToDatabaseColumn(Set list) {
if (list == null)
return null;
try {
return mapper.writeValueAsString(list);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public Set convertToEntityAttribute(String dbJson) {
if (dbJson == null)
return null;
try {
return mapper.readValue(dbJson, new TypeReference<Set<String>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
My fix for this issue was to separate the primary key with the foreign key. If you are using eclipse and made the above changes please remember to refresh the database explorer. Then recreate the entities from the tables.