I have an application with custom fields - users are basically able to define a custom field by selecting a type for the field and giving it a name. The custom fields are then presented as being part of an entity and data given to these fields is saved to my database. In most circumstances, I've been able to handle these programmatically and through the normal hibernate mappings (ie, #OneToMany annotated collection) without a problem. I'm currently faced with a problem, however. We'd like to have these custom fields and their values used for real-time reporting of the "parent" entities. Custom field values are mapped as collections inside the parent entities, but I need them flat for reporting purposes. I have created a view that provides exactly what I need from the SQL side of things - I followed this example to add dynamic pivoting and the resulting query is precisely how I'd like to display my information. Not the following images, of course, but that's essentially the output I have.
The view returns a completely dynamic number of columns, each named for a custom field and populated with the relevant data for that row.
The problem is that I now have no idea how to retrieve this information with Hibernate.
I found documentation for updating the PersistentClass by getting the ClassMappings from the Hibernate Configuration:
Manipulating metadata at runtime
//Get the existing mapping for AgreementsGrid from Configuration
PersistentClass gridMapping = configuration.getClassMapping(AgreementsGrid.class.getName());
//Define new Column
Column column = new Column();
column.setName("ESTIMATED_COST_OVERRUNS");
column.setNullable(true);
column.setUnique(false);
gridMapping.getTable().addColumn(column);
//Wrap the column in a value
SimpleValue value = new SimpleValue();
value.setTable(gridMapping.getTable());
value.setTypeName("string");
value.addColumn(column);
//Define new property for the AgreementsGrid class
Property prop = new Property();
prop.setValue(value);
prop.setName("customField1");
prop.setNodeName(prop.getName());
gridMapping.addProperty(prop);
//Build a new session factory for the new mapping
SessionFactory sessionFactory = configuration.buildSessionFactory();
I've only just realized that this is for Hibernate 3 & 4, and isn't even possible in Hibernate 5 (I'm using 5.2.18).
So, I'm trying to figure out how to handle this in Hibernate 5. I have a base entity mapped to a view, and at runtime I need to be able to dynamically add "fields" to it, so that my DAOs can dynamically filter the information and handle sorts/grouping.
Here is the entity I have for my view:
#Entity
#Table(name="AGREEMENTS_GRID")
public class AgreementsGrid implements Serializable {
private static final long serialVersionUID = 1L;
private Integer entityId;
#Column(name="ENTITY_ID")
#Id
public Integer getEntityId() {
return this.entityId;
}
public void setEntityId(Integer entityId) {
this.entityId = entityId;
}
private Agreements agreement;
#ManyToOne
#JoinColumn(name = "AGREEMENT_ID", referencedColumnName = "ID", nullable = false)
public Agreements getAgreement() {
return this.agreement;
}
public void setAgreement(Agreements agreement) {
this.agreement= agreement;
}
private BigDecimal expenditure;
#Column(name = "EXPENDITURE", nullable = true, precision = 22, scale = 2)
public BigDecimal getExpenditure() {
return this.expenditure;
}
public void setExpenditure(BigDecimal expenditure) {
this.expenditure = expenditure;
}
/*
* Dynamic fields would theoretically go here and look like this,
* for a custom field of type CURRENCY named 'Estimated Cost Overruns'
*/
/*
private BigDecimal customField1;
#Column(name = "ESTIMATED_COST_OVERRUNS", nullable = true, precision = 22, scale = 2)
public BigDecimal getCustomField1() {
return this.customField1;
}
public void setCustomField1(BigDecimal customField1) {
this.customField1 = customField1;
}
*/
}
Just to be clear, I cannot map these fields at compile time. They are purely custom and are defined entirely by users. At runtime, I will be able to know what custom fields do exist, so I would be able to loop through them and add them (as I hoped to do with the add column seen above), but I cannot know before deployment. The custom fields are also subject to change any moment.
For Hibernate 5 you should build MetaData via RegistryService, add property, then build SessionFactory via MetaData (Bootstrap native metadata). Something like this:
public SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sessionFactoryBuilder) {
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
registryBuilder.applySettings(sessionFactoryBuilder.getProperties());
Metadata metaData = getMetadataSources().buildMetadata(registryBuilder.build());
PersistentClass gridMapping = metaData.getEntityBinding(AgreementsGrid.class.getName());
Column column = new Column();
...
Property prop = new Property();
...
gridMapping.addProperty(prop);
SessionFactory sessionFactory = metaData.buildSessionFactory();
return sessionFactory;
}
I am conducting some Neo4J tests and running into the following peculiar problem. I created a small model which I'm intending to use with OGM. The model has a superclass Entity and a child class Child. They're both in package persistence.model. Entity has the required Long id; with matching getId() getter.
public abstract class Entity {
private Long id;
public Long getId() {
return id;
}
}
#NodeEntity
Child extends Entity {
String name;
public Child() {
}
}
Creating Child objects and persisting them through OGM works fine. I'm basing myself on the examples found in the documentation and using a Neo4jSessionFactory object, which initialises the SessionFactory with the package persistence.model. The resulting database contains objects with proper ID's filled in.
The problem arises when I try to fetch a Child for a given ID. I'm trying it with three methods, using two connection systems (bolt and ogm):
boltSession.run("MATCH (a:Child) WHERE id(a) = {id} RETURN a", parameters("id", childId));
ogmSession.query("MATCH (a:Child) WHERE id(a) = $id RETURN a", params);
ogmSession.load(Child.class, childId, 1);
The first two methods actually return the correct data. The last one returns a null value. The last one, using OGM, has some obvious benefits, and I'd love to be able to use it properly. Can anyone point me in the right direction?
In your test code you are doing a lookup by id of type int.
private int someIdInYourDatabase = 34617;
The internal ids in Neo4j are of type Long.
If you change the type of the id to long or Long then it will work.
private long someIdInYourDatabase = 34617;
I do have a Repository
#Repository
public interface PointOfInterestRepository extends GraphRepository<Poi> {
// currently empty
}
with no custom methods defined. So I use the like of save(T... entities) which are predefined.
And I have my Poi class as follows
#NodeEntity(label = "PointOfInterest")
public class Poi {
#JsonIgnore
#GraphId
Long neo4jId;
#JsonManagedReference("node-poi")
#JsonProperty("node")
#Relationship(type = "BELONGS_TO", direction = Relationship.UNDIRECTED)
private Node node;
#JsonProperty("id")
#Property(name = "poiID")
private final String id;
#JsonProperty("uris")
#Property(name = "uris")
private final Set<URI> correspondingURIs = new HashSet<>();
/* Some more stuff I skip here*/
}
with getters for the fields.
Currently I am able to save such Pois to neo4j and retrieve them back, but when I try to work with those Nodes in the database via cypher it appears that the fields aren't mapped to neo4j properties.
I thought spring-data-neo4j would convert my class fields to neo4j graph properties. Am I wrong with that?
Note: The save calls seems to work very well. After that I can see the Nodes in the database and calling findAll() afterwards will return me all the saved Nodes (Pois) properly with all the correct values. But somehow, within the database, I cannot see any properties/fields.
The problem is the final fields. SDN would be unable to write values back to the entity when loaded from the graph because these fields are final (and SDN will use only the default no-args constructor), and as such, final fields are not supported.
Removing the final should fix this.
I have a class which is mapped to a table using the hibernate notations of auto increment. This class works fine when I set values and update this to the database and I get a correct updated value in the table.
But the issue is when I create a new object of this class and try to get the id, it returns me a 0 instead of the auto_incremented id.
The code of the class is
#Entity(name="babies")
public class Baby implements DBHelper{
private int babyID;
#Id
#Column(name="babyID", unique=true, nullable= false)
#GeneratedValue(strategy = GenerationType.AUTO)
public int getBabyID() {
return babyID;
}
public void setBabyID(int babyID) {
this.babyID = babyID;
}
}
The code I use to get the persistent value is
Baby baby = new Baby();
System.out.println("BABY ID = "+baby.getBabyID());
This returns me a
BABY ID = 0
Any pointers would be appreciated.
Thanks,
Sana.
Hibernate only generates the id after an entity becomes persistent, ie after you have saved it to the database. Before this the object is in the transient state. Here is an article about the Hibernate object states and lifecycle
The ID is set by hibernate when object is saved and became persistable.
The annotation are only informing hibernate, how he should behave with class, property, method that annotation refer to.
Another thing if You have current id value how hibernate, would be able to recognize that he should insert or only update that value.
So this is normal expected behavior.
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.