Arbitrary 'columns' from HashMap in hibernate query - java

Suppose I have an entity User
public class User {
private String username;
private String password;
private Map<String, Object> settings = new HashMap<String, Object>();
//Getters & setters
}
Is there any way that I can use the key of the settings map in a query?
Eg:
Criterion crit = mySession.createCriterion(User.class);
crit.add(Restrictions.eq("settings.blah", 1234L));
List<User> results = crit.list();
Or access the map values via EL?
Eg:
<h:dataTable value="#{myBean.users}" var="user">
<h:column>
<h:outputText value="#{user.blah}"/>
</h:column>
</h:dataTable>
I can partially do this with a simple #OneToMany mapping, but that required my query to use both key and value, and the properties are not accessible through EL.
Is something like this possible? How would I map it with annotations?
Thanks
EDIT:
Essentially, what I am after is a 'vertical table' implementation so that I can have arbitrary fields, and still be able to use the Entity as a mapped Class. I do not know if this is even possible with hibernate.

I never user Object as a value of the map, you can use String with this mapping:
#Entity
public class User {
private int _id;
private Map<String, String> _settings;
public User() {
}
#Id
#GeneratedValue
public int getId() {
return _id;
}
public void setId(int id) {
_id = id;
}
#ElementCollection
#MapKeyColumn(name = "SETTINGS_KEY")
#CollectionTable(name = "USER_SETTINGS", joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "SETTING")
public Map<String, String> getSettings() {
return _settings;
}
public void setSettings(Map<String, String> settings) {
_settings = settings;
}
}
If you need to use different type of columns then you must use a interface as value of the map like this:
#Entity
public class TestSettings {
private int _id;
private Map<String, SettingValue<?>> _settings;
public TestSettings() {
}
#Id
#GeneratedValue
public int getId() {
return _id;
}
public void setId(int id) {
_id = id;
}
#OneToMany
#MapKeyColumn(name = "SETTINGS_KEY")
#CollectionTable(name = "USER_SETTINGS", joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "SETTING")
public Map<String, SettingValue<?>> getSettings() {
return _settings;
}
public void setSettings(Map<String, SettingValue<?>> settings) {
_settings = settings;
}
}
The SettingValue abstract class:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class SettingValue<V> implements Serializable {
private static final long serialVersionUID = 3355627640146408150L;
private Integer _id;
public SettingValue() {
super();
}
#Id #GeneratedValue
public Integer getId() {
return _id;
}
public void setId(Integer id) {
_id = id;
}
#Transient
public abstract V getValue();
public abstract void setValue(V value);
}
The settingValue String implementation:
#Entity
#PrimaryKeyJoinColumn(name="settingvalue_id", referencedColumnName="id")
public class TextSettingValue extends SettingValue<String> implements Serializable {
private String _value;
public TextSettingValue() {
super();
}
#Override
public void setValue(String value) {
_value = value;
}
#Override
public String getValue() {
return _value;
}
}
You have to create a SettingValue implementation for each type you want to support, in this way you get a table for each type with a column to hold your value.

Related

How to map a class that contains a list of objects of various type to a collection of tables with Hibernate?

I have a class that is called Element and it is subclassed by User and Entity that have some common fields like value, id and uuid and some others that are not common..
#Entity
class Element {
#Id #GeneratedValue
private Long id;
private String uuid;
private String value;
}
#javax.persistence.Entity
class Entity extends Element {
private String description;
}
#Entity
class User extends Element {
private String password;
}
I also have a class Information:
#Entity
class Information {
#Id #GeneratedValue
private Long id;
private List<Element> elements;
private String information_field1;
private String information_field2;
}
elements can contain Users and Entities
How would you suggest I should map that with Hibernate..
I was thinking that in the database I could have those tables
Element
----------------
information_id, user_id, entity_id, order
0, null, 10, 0
0, 12, null, 1
Information
----------------
id, information_field1, information_field2
0, "some value", "some other value"
That represents Information with id 0, having first (order = 0) Element being an Entity with id 10 and second (order = 1) Element being a User with id 12.
I don't mind a completely different db design as long as it doesn't lose information but the class structure isn't possible to be changed. I know how to make it work with classes that are exactly the same as the rows of the tables I suggested. I am curious if Hibernate annotations are sophisticated enough so when I store Information it will insert the appropriate rows, ids and order in the two tables.
Thanks!
Hibernate documentation https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/collections.html#collections-mapping explains how to map collections. You must use #OneToMany and #JoinTable annotations.
#Entity
class Information {
#Id #GeneratedValue
private Long id;
#OneToMany
#JoinTable(
name="INFORMATION_ELEMENTS",
joinColumns = #JoinColumn( name="INFORMATION_ID"),
inverseJoinColumns = #JoinColumn( name="ELEMENT_ID")
)
private List<Element> elements;
private String information_field1;
private String information_field2;
}
This will create a table INFORMATION_ELEMENTS that contains the Information to Element mappings. You don't need to maintain INFORMATION_ID in the Element table.
So this is what worked for me
#javax.persistence.Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Element {
#Id
#GeneratedValue
#Column(name = "element_id")
private Long id;
#Column(name = "uuid", nullable = false)
private String uuid;
#Column(name = "value")
private String value;
public Element(String value) {
this.value = value;
}
public Element() {
}
#PrePersist
public void initUuid() {
this.uuid = UUID.randomUUID().toString();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
#PrimaryKeyJoinColumn(name="element_id")
public class Entity extends Element {
private String description;
public Entity() {
super();
}
public Entity(String value, String description) {
super(value);
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#PrimaryKeyJoinColumn(name="element_id")
public class User extends Element {
public User() {
super();
}
public User(String value) {
super(value);
}
}
#javax.persistence.Entity
public class Information {
#Id
#GeneratedValue
#Column(name = "information_id")
private Long id;
private String uuid;
private String value;
private String innerText;
#ManyToMany(cascade = {CascadeType.MERGE,CascadeType.REFRESH} )
private List<Element> elements;
public Information() {
}
public Information(String innerText, List<Element> elements) {
this.innerText = innerText;
this.elements = elements;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getInnerText() {
return innerText;
}
public void setInnerText(String innerText) {
this.innerText = innerText;
}
public List<Element> getElements() {
return elements;
}
public void setElements(List<Element> elements) {
this.elements = elements;
}
}
I then extend the CrudRepository interfaces:
public interface InformationDao extends CrudRepository<Information,Long> {
List<Information> findAll();
}
public interface ElementDao extends CrudRepository<Element, Long> {
List<Element> findAll();
Element findByValue(String value);
Element findById(Long id);
}
And for testing purposes I have this Service.
public class InformationService {
#Autowired
ElementDao elementDao;
#Autowired
InformationDao infoDao;
#Transactional
public void init() {
List<Element> elements = new ArrayList<>();
Element element1 = new Entity("test", "description");
Element element2 = new User("test2");
Element element3 = new Entity("test3", "description");
Element element4 = new User("test4");
elements.add(element1);
elements.add(element2);
elements.add(element3);
elements.add(element4);
elementDao.save(elements);
Information info = new Information("some text", elements);
infoDao.save(info);
List<Element> elements2 = new ArrayList<>();
Element element5 = elementDao.findById(element1.getId());
Element element6 = elementDao.findById(element2.getId());
Element element7 = elementDao.findById(element3.getId());
Element element8 = elementDao.findById(element4.getId());
elements2.add(element5);
elements2.add(element6);
elements2.add(element7);
elements2.add(element8);
elementDao.save(elements2);
Information info2 = new Information("# something else #", elements2);
//failTransaction();
infoDao.save(info2);
}
public void failTransaction() {
throw new RuntimeException("transaction failed");
}
}
Keep in mind that due to
#ManyToMany(cascade = {CascadeType.MERGE,CascadeType.REFRESH} )
private List<Element> elements;
on Information, all Elements need to be already pre-stored in the database. I couldn't make it to work with CascadeType.ALL that stores children Element all the time when the parent element is stored. I was getting detached entity passed to persist when I was trying to store info2

Spring and Jackson: set json ignore dynamically

I have some JPA models: "Category" and "Article":
#Entity
#Table(name = "categories")
public class Category {
private int id;
private String caption;
private Category parent;
private List<Category> childrenList;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
#ManyToOne
#JoinColumn(name = "parent_id")
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
#OneToMany
#JoinColumn(name = "parent_id")
public List<Category> getChildrenList() {
return childrenList;
}
public void setChildrenList(List<Category> childrenList) {
this.childrenList = childrenList;
}
}
#Entity
#Table(name = "articles")
public class Article {
private int id;
private String caption;
private boolean isAvailable;
private String description;
private int price;
private Category category;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
#Column(name = "is_available")
#Type(type = "org.hibernate.type.NumericBooleanType")
public boolean getIsAvailable() {
return isAvailable;
}
public void setIsAvailable(boolean available) {
isAvailable = available;
}
#Column
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Column
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
#ManyToOne
#JoinColumn(name = "category_id")
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
Also i have some REST controller with two methods:
1)In the first method i need to get and serialize last 10 Articles, but i don't need "childrenList" and "parent" field in Categegory.
2)In the second method i need to get the same but serialize "parent" field.
How can i solve this?
If i will use #JsonIgnore annotation to these fields then they will be never serialized.
Or should i use DTO classes?
How can i dynamically set field for ignoring?
I never use my Entitys for generating JSON, I think another set DTO classes will make you happier in the long run. My DTO typically has a constructor which takes the Entity as argument (it still needs a default constructor if you plan to use it for parsing incoming JSON).
If you really want to use your Entities, I would recommend that you use MixIns, which allows you to register a MixIn class, that augments the serialization of a specific class.
Here is a link to a MixIn example I made for another answer.
Use a custom serializer, the psedo code is below.
public class CategorySerializer extends StdSerializer<Category> {
public CategorySerializer() {
this(null);
}
public CategorySerializer(Class<Category> t) {
super(t);
}
#Override
public void serialize(Category value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
// put the logic here to write the parent and child value or not
// here is the example to how the data is serialized
jgen.writeStartObject();
jgen.writeNumberField("id", value.id);
jgen.writeStringField("caption", value.caption);
jgen.writeEndObject();
}
}
Now, to utilize the custom serializer put this annotation above your Catagory entity class.
#JsonSerialize(using = CategorySerializer.class)

Spring JPA - Data integrity relationships

I'm new to Java and even more newer to Spring (Boot and JPA) but I was curious, I'm trying to debug an issue that says, "No identifier specified for entity".
For illustartion purposes, I've created the following tables from this diagram:
Originally, there was a M:N relationship between the user and vehicle table, so I created an associative entity (UserVehicleAsso) to split the two up. I was following this guide on M:N mapping in Java, http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/
For the most part, it was pretty straight forward but my question is, within the associative entity (UserVehicleAsso), do I have to use the #Id annotation for each of the foreign keys? I assume that I didn't need to because those were automatically generated from each of the respective tables.
Let me know your thoughts or comments, thanks.
Also, below is the code that I used to generate these models:
For the User table/class:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String fName;
private String lName;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="userVehicleAsso",
joinColumns={#JoinColumn(name="userID")},
inverseJoinColumns={#JoinColumn(name="vehicleID")})
private Set<Vehicle> vehicles = new HashSet<Vehicle>();
//constructor
protected User() {}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getFName() {
return fName;
}
public void setFName(String fName) {
this.fName = fName;
}
public String getLName() {
return lName;
}
public void setLName(String lName) {
this.lName = lName;
}
public Set<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(Set<Vehicle> vehicles) {
this.vehicles = vehicles;
}
#Override
public String toString() {
return getFName() + "," + getLName();
}}
For the Vehicle table/class:
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int vehicleId;
private String brand;
private String model;
//foreign key mappings
//mapping with associative
#ManyToMany(mappedBy="vehicles")
private Set<User> users = new HashSet<User>();
//constructors
protected Vehicle() {}
public Vehicle(int id) {
this.vehicleId = id;
}
public Vehicle (String brand, String model) {
this.brand = brand;
this.model = model;
}
/* public Vehicle() {
}*/
public int getVehicleId() {
return vehicleId;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public void setVehicleId(int vehicleId) {
this.vehicleId = vehicleId;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#Override
public String toString() {
// + setBodyType() + "," +
return getBrand() + "," + getModel();
}
}
And then finally, my associtive table/class:
#Entity
public class UserVehicleAsso{
private int userID;
private int vehicleID;
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public int getVehicleID() {
return vehicleID;
}
public void setVehicleID(int vehicleID) {
this.vehicleID = vehicleID;
}
}
In my opinion, it's not necessary to have an Entity class for the middle table in your case. The table will be generated automatically if configured correctly. In this table, there would not be column ID, only two columns with userID and vehicleID data.
Now, if your middle table has more than what are needed to establish the M:N relationship, then your middle Entity class is needed, and the ID of it, too. For example, if this class is intended to store the time stamp every time a relationship is established, you have to:
Create this Entity class,
Give it an ID field with proper generation strategy,
Map the time stamp with a field with adequate type, annotation/XML mapping and so on.
This part of JPA/Hibernate have confused me a lot and I used to get into them. If my memory serves me well this is the proper/perfect way how things should work.
You can specify a composite primary key class that is mapped to multiple fields or properties of the entity.
Here are sample codes:
public class ActivityRegPK implements Serializable {
private int activityId;
private int memberId;
public int getActivityId() {
return activityId;
}
public void setActivityId(int activityId) {
this.activityId = activityId;
}
public int getMemberId() {
return memberId;
}
public void setMemberId(int memberId) {
this.memberId = memberId;
}
}
associtive table/class:
#IdClass(ActivityRegPK.class)
#Entity
#Table(name="activity_reg")
#NamedQuery(name="ActivityReg.findAll", query="SELECT a FROM ActivityReg a")
public class ActivityReg implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="activity_id")
private int activityId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="ins_date")
private Date insDate;
#Id
#Column(name="member_id")
private int memberId;
}
Activity.class
#Entity
#NamedQuery(name="Activity.findAll", query="SELECT a FROM Activity a")
public class Activity implements Serializable {
// some attributes
}

Getting #Id of lazy-loaded #ManyToOne entity allways returns null

I am using Hibernate 4.3.8.Final and have problem with retrieving #Id property of lazy fetched property: For attached classes calling aidConfiguration.getChipApplication().getId() allways returns null. Other properties, eg. aidConfiguration.getChipApplication().getVersion() returns correctly the value from DB. If chipApplication is not lazy loaded (see the comment in the code), then aidConfiguration.getChipApplication().getId() returns correct non-null value.
What am I dong wrong?
BTW I need it to be lazy.
BaseEntity:
#MappedSuperclass
public class BaseEntity implements Serializable {
#Id
#Column(name = "ID", unique = true)
#Size(min = 1, max = 255)
private String id;
#PrePersist
public final void generateUuid() {
if (this.getId() == null) {
this.setId(UUID.randomUUID().toString());
}
}
public final String getId() {
return id;
}
public final void setId(final String id) {
this.id = id;
}
}
AidConfiguration:
#Entity
#Audited
public class AidConfiguration extends BaseEntity {
#Column
#NotBlank
private String name;
#NotNull
#ManyToOne(fetch = FetchType.LAZY) // if it is EAGER (defaut) then then aidConfiguration.getChipApplication().getId() returns correctly non-null value
private ChipApplication chipApplication;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "aidConfiguration", cascade = CascadeType.ALL) // cascade for auto-saving and deleting items
private List<AidConfigurationItem> aidConfigurationItems;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public ChipApplication getChipApplication() {
return chipApplication;
}
public void setChipApplication(final ChipApplication chipApplication) {
this.chipApplication = chipApplication;
}
public List<AidConfigurationItem> getAidConfigurationItems() {
return aidConfigurationItems;
}
public void setAidConfigurationItems(final List<AidConfigurationItem> aidConfigurationItems) {
this.aidConfigurationItems = aidConfigurationItems;
}
}
ChipApplication:
#Entity
#Audited
public class ChipApplication extends BaseEntity {
#Column
#NotBlank(message = "Aid can not be empty")
private String aid;
#Column
#NotBlank(message = "Product can not be empty")
private String product;
#Column
#NotBlank(message = "Version can not be empty")
private String version;
#NotNull(message = "Network is mandatory")
#ManyToOne(fetch = FetchType.LAZY)
private Network network;
#ManyToMany(fetch = FetchType.EAGER)
private List<AidTag> aidTags;
public String getAid() {
return aid;
}
public void setAid(final String aid) {
this.aid = aid;
}
public String getProduct() {
return product;
}
public void setProduct(final String product) {
this.product = product;
}
public String getVersion() {
return version;
}
public void setVersion(final String version) {
this.version = version;
}
public Network getNetwork() {
return network;
}
public void setNetwork(final Network network) {
this.network = network;
}
public List<AidTag> getAidTags() {
return aidTags;
}
public void setAidTags(final List<AidTag> aidTags) {
this.aidTags = aidTags;
}
}
Bit late, but the issue HH-9588 is still unresolved, and I just had the same issue (XML mapping rather than annotations, though).
Could not get the id from the getter when the binding was lazy. Got it when eager or fetch join.
Fixed it by getting rid of the "final" modifier on the getId() accessor. (final here was an attempt to protect the way primary keys/identifiers are defined in the superclass for all the entities)
before :
public abstract class Foo {
Long id;
public final Long getId() {
return id;
}
protected final void setId( Long id ){
this.id = id;
}
...
after :
public abstract class Foo {
Long id;
// No more final
public Long getId() {
return id;
}
// No more final
protected void setId( Long id ){
this.id = id;
}
...
Now, I can get the Id with a lazy binding as well.
Seems to me that this "final" modifier does not allow Hibernate to proxy this accessor as intended. The other accessors being not "final", one can access their values from the proxy.
So, I wonder whether HH-9588 is really a bug or a misunderstanding of the hibernate ways ?
That seems a bug, if you do not miss anything. I would report it on Hibernate's bug tracking system. It would be nice if you would update this answer afterwards with a link to the bug.

Hibernate #id composite

I am modeling a database.
There is TRIPLE which contains three CONCEPT. So the primary key of the class TRIPLE is three uri all together. (One concept could be in different TRIPLE).
Also TRIPLE is related with another class, ANNOTATION, and here is the question, how can triple_id be identified?? But first of all, if building this Id composite is correct.
To model that:
Concept.java
#Entity
#Table(name = "concept")
public class Concept implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String id;
private List<TripleDBModel> triples;
#ManyToMany(
cascade={CascadeType.ALL},
fetch=FetchType.LAZY,
mappedBy = "concepts"
)
public List<TripleDBModel> getTriples() {
return triples;
}
public void setTriples(List<TripleDBModel> triples) {
this.triples = triples;
}
ConceptPk.java
#Embeddable
public class ConceptPk implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String uri;
public ConceptPk(String uri, String label){
this.uri = uri;
}
public ConceptPk(){
super();
}
#Id
#Column(name = "uri", length = 100, unique = true, nullable = false)
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
Triple.java
#Entity
#IdClass(ConceptPk.class)
#Table(name = "triple")
public class TripleDBModel {
protected List<Annotation> annotations;
protected String conceptUriSubject;
protected String conceptUriObject;
protected String conceptUriPredicate;
#ManyToMany(
cascade={CascadeType.ALL},
fetch=FetchType.LAZY
)
#JoinTable(name = "triple_has_concept",
joinColumns=#JoinColumn(name="uri"),
inverseJoinColumns=#JoinColumn(name="triple_id")) //What shoul I write here???
public List<Annotation> getAnnotations() {
return annotations;
}
public void setAnnotations(List<Annotation> annotations) {
this.annotations = annotations;
}
#Id public String getConceptUriSubject() {
return conceptUriSubject;
}
public void setConceptUriSubject(String conceptUriSubject) {
this.conceptUriSubject = conceptUriSubject;
}
#Id public String getConceptUriObject() {
return conceptUriObject;
}
public void setConceptUriObject(String conceptUriObject) {
this.conceptUriObject = conceptUriObject;
}
#Id public String getConceptUriPredicate() {
return conceptUriPredicate;
}
public void setConceptUriPredicate(String conceptUriPredicate) {
this.conceptUriPredicate = conceptUriPredicate;
}
}
Thanks in advance!!
You could use an Id class like this:
class TripleId implements Serializable {
#Column(...)
private String conceptUriSubject;
#Column(...)
private String conceptUriObject;
}
And use it in Triple:
#Entity
#Table(name = "triple")
public class TripleDBModel {
#EmbeddedId
private TripleId id;
...
}
Also note that you can provide multiple join columns:
inverseJoinColumns= {#JoinColumn(name="subjectUri"), #JoinColumn(name="objectUri"), ... }

Categories

Resources