I'm just starting to learn Java database persistence using Hibernate ORM and have run into a problem which I haven't been able to resolve.
I have these two classes:
#Embeddable
public class Resource {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Resource() {
}
}
#Entity
public class Group {
#Embedded
private Map<String, Resource> resources;
#Id
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, Resource> getResources() {
return resources;
}
public void setResources(Map<String, Resource> resources) {
this.resources = resources;
}
public Group() {
resources = new HashMap<String, Resource>();
}
}
The Resource shouldn't have its own table because it shouldn't exist outside the Group scope. That's why I used the Embeddable, to be treated as a component.
To sum up I'd like to know how I can store these classes in a database using Hibernate ORM. The Resource class shouldn't be an Entity as it doesn't need its own class.
And I would prefer to use the mapping notations and not XML files.
As it is I get this error:
Syntax error in SQL statement "INSERT INTO GROUP[*] (NAME) VALUES (?) "; expected "identifier"; SQL statement:
It is possible to save all instances of Resource belonging to certain Group to the same database row with Group itself by creating class that wraps inside groups and saving it as Serializable to BLOB field in database.
Because such a solution asks more code and produces confusing data model, it is unlikely that you really want to limit yourself to have only one table.
If, as you said, Resource is fully owned by Group and you want to access them by some string key then #ElementCollection containing instances of embeddable Resource is solution (assuming that your version of Hibernate already have it):
#ElementCollection
private Map<String, Resource> resources;
If you do not have access collection of Resource by name, then following is sufficient
#ElementColection
private Set<Resource> resources;
For more examples about fine tuning your element collection can be found from: Java Persistence/ElementCollection
Related
I try to map my DTO objects to my JPA entities. I have a Collection of children in my ParentEntity. They can be added addChild(). Using the Adder is supported by Mapstruct via the CollectionMappingStrategy (http://mapstruct.org/documentation/dev/reference/html/#collection-mapping-strategies).
This works fine if I create new entities, but fails to clear the children on updating before adding the new children.
The Mapstruct manual says (http://mapstruct.org/documentation/dev/reference/html/#updating-bean-instances):
Collection- or map-typed properties of the target bean to be updated will be cleared and then populated with the values from the corresponding source collection or map.
What am I missing? Is there an additional option I have to set? There is a full example with test case to reproduce the problem at https://github.com/davidfuhr/mapstruct-jpa-child-parent
Here are the classes:
public class ParentEntity {
private String name;
private List<ChildEntity> children = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildEntity> getChildren() {
return children;
}
public void addChild(ChildEntity child) {
children.add(child);
child.setMyParent(this);
}
public void removeChild(ChildEntity child) {
children.remove(child);
child.setMyParent(null);
}
}
public class ChildEntity {
private String name;
private ParentEntity myParent;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentEntity getMyParent() {
return myParent;
}
public void setMyParent(ParentEntity myParent) {
this.myParent = myParent;
}
}
public class ParentDto {
private String name;
private List<ChildDto> children;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildDto> getChildren() {
return children;
}
public void setChildren(List<ChildDto> children) {
this.children = children;
}
}
public class ChildDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
ParentEntity toEntity(ParentDto s);
ParentEntity updateEntity(ParentDto s, #MappingTarget ParentEntity e);
#Mapping(target = "myParent", ignore = true)
ChildEntity toEntity(ChildDto s);
}
The text in the documentation need to be rephrased. The problem is that especially for collections, there's no good way to handle this out of the box in MapStruct. I'm currently writing some new text for the documentation.
Consider this (when thinking what MapStruct should do for updating collections in general):
What if there's no match: should the non-matching elements be removed?
Should the non matching source elements be added?
What exactly constitutes to a match: equals? hashcode? comparator==0?
Can there be more than one match (Lists, but also depending on what is considered a match.)
How should the resulting collection be sorted?
Should a newly created object be added to a persistence context?
What about JPA child-parent relations?
About the latter one, Dali (Eclipse) also generates remove methods. So should MapStruct call these in the light of the above?
At this moment it works like this: whenever the user wants a collection update method, MapStruct generates a regular call to element mappings (in stead of an update call), because it is the only sensible thing to do. All the remainder is highly dependent on the use-case. If you need to clear the collection at before hand, use the #BeforeMapping to clear it.
Note: I just fixed an issue that handles also adders in this fashion in stead of the vague error message you get now.
If you want a nice way to handle child/parent relations and integrate them with JPA.. have a look at the examples.
I was trying to use Spring Data JPA on Spring Boot and I kept getting error, I can't figure out what the problem is:
Unable to locate Attribute with the the given name [firstName] on
this ManagedType [com.example.h2demo.domain.Subscriber]
FirstName is declared in my entity class. I have used a service class with DAO before with different project and worked perfectly.
My Entity class (getters and setters are also in the class) :
#Entity
public class Subscriber {
#Id #GeneratedValue
private long id;
private String FirstName,LastName,Email;
public Subscriber(long id, String firstName, String lastName, String email) {
this.id = id;
this.FirstName = firstName;
this.LastName = lastName;
this.Email = email;
}
}
...
My Repository Class
#Component
public interface SubscriberRepository extends JpaRepository<Subscriber,Long> {
Subscriber findByFirstName(String FirstName);
Subscriber deleteAllByFirstName(String FirstName);
}
My Service Class
#Service
public class SubscriberService {
#Autowired
private SubscriberRepository subscriberRepository;
public Subscriber findByFirstName(String name){
return subscriberRepository.findByFirstName(name);
}
public Subscriber deleteAllByFirstName(String name){
return subscriberRepository.deleteAllByFirstName(name);
}
public void addSubscriber(Subscriber student) {
subscriberRepository.save(student);
}
}
And My Controller class:
#RestController
#RequestMapping("/subscribers")
public class SubscriberController {
#Autowired
private SubscriberService subscriberService;
#GetMapping(value = "/{name}")
public Subscriber findByFirstName(#PathVariable("name") String fname){
return subscriberService.findByFirstName(fname);
}
#PostMapping( value = "/add")
public String insertStudent(#RequestBody final Subscriber subscriber){
subscriberService.addSubscriber(subscriber);
return "Done";
}
}
Try changing private String FirstName,LastName,Email; to private String firstName,lastName,email;
It should work.
findByFirstName in SubscriberRepository tries to find a field firstName by convention which is not there.
Further reference on how properties inside the entities are traversed https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
The same problem was when i had deal with Spring Data Specifications (https://www.baeldung.com/rest-api-search-language-spring-data-specifications)
Initial piece of code was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("project_number"), "%" + projectNumber)
}
The problem was in root.get("project_number"). Inside the method, I had to put the field name as in the model (projectNumber), but I sent the field name as in the database (project_number).
That is, the final correct decision was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("projectNumber"), "%" + projectNumber)
}
After I change my entity class variables from capital letter to small letter for instance Username to username the method Users findByUsername(String username); is working for me now .
As per specification , the property names should start with small case.
...The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized)....
It will try to find a property with uncapitalized name. So use firstName instead of FristName and etc..
I have a customer's database that has a collection, in which the document fields can vary between each other. There are some constant fields I can rely on, but as for the rest - I have no way of narrowing the field list as the customer wants the solution to be dynamic.
My question is - can I somehow implement a generic mapping that would return, let's say, a map of document's fields using Spring Data?
edit:
Thanks for the tips. I've tried getting the generic Object (hoping I'd be able to convert it into a map) using the entity:
#Document(collection = "Data")
public class DataEntity {
#Id
private String id;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
but fetching the object by the ID using MongoRepository produces an object with data field set to null.
I'm using SpringBoot 1.3.1.RELEASE with spring-boot-starter-data-mongodb 1.3.1.RELEASE.
You can use a Map for dynamic properties like below. Is this what you are looking for?
#Document(collection = "computers")
public class Computer {
#Id
private String id;
#Field("name")
private String name;
//Other constant fields
#Field("properties")
private Map<String, Object> properties;
}
I have 5 different tables in my database on MS SQL Server 2012. I have created class for my UserTable and filled in setters and getters shown below , is it logical to put other tables's setters and getters inside the same glass or create separate class for other tables with setters and getters.
import java.math.BigDecimal;
import java.sql.Connection;
public class UserFR17setget {
Connection cn;
BigDecimal userID;
String UserName;
String UserPassword;
int UserSecurity;
BigDecimal ProjectID;
public BigDecimal getUserID() {
return userID;
}
public void setUserID(BigDecimal userID) {
this.userID = userID;
}
public String getUserName() {
return UserName;
}
public void setUserName(String UserName) {
this.UserName = UserName;
}
public String getUserPassword() {
return UserPassword;
}
public void setUserPassword(String UserPassword) {
this.UserPassword = UserPassword;
}
public int getUserSecurity() {
return UserSecurity;
}
public void setUserSecurity(int UserSecurity) {
this.UserSecurity = UserSecurity;
}
public BigDecimal getProjectID() {
return ProjectID;
}
public void setProjectID(BigDecimal ProjectID) {
this.ProjectID = ProjectID;
}
}
Regards
You're basically implementing a part of JPA yourself. In JPA a class maps to a database table, and you think in terms of entities instead of database tables. You might want to explore that later on (unless you wish to do so now).
If you just want to map database data into objects, you have a few reasonable choices. Do as you're doing now, make each database table have a related class (User is a lot better name for a class than UserFR17getset by the way), then you can manipulate the data as objects. A more higher level approach is to have the classes reference to other tables, so your User class would contain an Address reference (assuming there's a table Address), and when loading a User, it would also load the Address. This is similar to how JPA works, and then you would be able to do a single "load user 1" instead of "load user 1, load address for user 1". Of course you would be responsible for implementing the actual loading (unlike with JPA, where there's a lot of automation going on).
Don't make the Connection object a part of your class though, it has no business being there. You should use the Connection object with your loading method, such as User u = loadUser(connection, userId);
I'm currently working good object oriented principles, and hibernate, I have this POJO, in which the properties would be dynamically populated. This is a design pattern I've read for good Object Oriented Design, in which it would be easy to add attributes to specificic object without breaking the application. My question is, how can you map this to a table, when your attributes is supposedly dynamic, I'm using an enum to limit the key value pairs for the map, but ideally it can still grow. I am only using in-memory database (h2) and I'm not going to be using the code for production use. This is for learning purposes only. see code below:
public class Transaction {
private static Map<Object, Object> properties;
public Transaction(){
if(null != properties)
properties = new LinkedHashMap<Object, Object>();
}
public Transaction(Map<Object, Object> properties){
if(null != properties)
setProperties(properties);
}
public void setProperties(Map<Object, Object> prop){
properties = prop;
}
public void setProperties(Properties property, String value){
properties.put(property, value);
}
public Map<Object, Object> getProperties(){
return properties;
}
public String getProperties(Properties property){
return (String) properties.get(property);
}
}
So I want to be able to create a table that would have this properties, dynamically,
My Enum:
public enum Properties {
Entry("Entry"), Id("Entry_ID"), Name("Name"), Credit("Credit");
private final String description;
private Properties(final String description){
this.description = description;
}
#Override
public String toString(){
return description;
}
}
I have this hibernate mapping, but as you can see this would be need to be updated everytime a field is updated, I need a generic mapping so that when I change/add the attributes, annotation or xml would be okay, see below:
<class name="Transaction" table="TRANSACTION">
<id name="id" column="ENTRY_ID">
<generator class="increment"/>
</id>
<property name="name"/>
<property name="credit" type="boolean" column="IS_CREDIT"/>
</class>
UserDefinedField on Marin Fowler's web site may be a perfect starting point for exploring general answers to this question.
As for Hibernate: It's really designed for statically binding tables to objects and you may have significant problems if you change the schema while running. You can implement the following solutions, though:
Serialized LOB (you serialize your Map into a binary field or - using JSON/XML - a text field). This is a half-and-half approach - half tabular/normal form/SQL and half not-SQL. So, if this approach is attractive, you might want to consider going all-in with a NoSQL database as discussed later
Attribute table, where your customized attributes are stored in a key-value pair table that joins to to the master table. This can be mapped in Hibernate using Indexed Collections (see section 7.2.2.2 Maps) and you would end up with something quite like in your question:
#Entity
public class Transaction {
#Id #GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
// ... snip ...
#OneToMany(mappedBy="transaction")
#MapKey(name="name")
public Map<String, String> getProperties(){
return properties;
}
public void setProperties(Map<String, String> prop){
properties = prop;
}
private Map<String, String> properties; // NB: Type has to be <String, String> because the column name is a String and you have defined the property value to be a String.
public void setProperty(Properties property, String value){
properties.put(property, value);
}
public String getProperty(String name){
return (String) properties.get(property);
}
}
#Entity
public class Property {
#Id #GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
#ManyToOne
public Transaction getTransaction() { return transaction; }
public void setTransaction(Transaction transaction) { this.transaction = transaction; }
private Transaction transaction;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
private String name;
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
private String description;
}
Pre-defined custom-fields, where you start with a really wide table with loads of unused columns. In this implementation you end up defining a mapping between your arbitrary property names and the pre-defined column names (getString1(), getString10(), etc)
However, a much better solution for you may be to use a NoSQL database - specifically a document-based one. These allow you to store and retrieve arbitrary data-structures (maps and lists). Interestingly, using such an approach makes binding to the data store significantly easier.
MongoDB or Redis (Java bindings at Jedis) are examples.