Sorry but I'm so newbie to MongoDB and Spring-data and i have a question about these two classes :
#Document(collection = "person")
public class Person {
/**
*
*/
private static final long serialVersionUID = 6268875544266598239L;
#Id
private String Id;
private String name;
//#DBRef(db = "mail")
ArrayList<Mail> mails = new ArrayList<Mail>();
and the other one is :
#Document(collection = "mail")
public class Mail {
/**
*
*/
private static final long serialVersionUID = 9149555841222037638L;
#Id
private String Id;
private String mail;
Person person;
Will I have a problem in referencing in this case ? I mean do i need to put #DBref or #Reference or any other annotation to work as for the #ManyToOne annotation in JPA? I saw many exemples but i cant get the point does it work without any annotation ?
Without annotation it will save mail objects inline in the person documents. Viceversa with the annotation it will save the mail DBRefs in the person documents.
Example:
person document without #DBRef in: {"id":"foo", "name":"bar", "mails":[{"id":"abc", "mail":"hello"},{"id":"def","mail":"world"}]}
person document with #DBRef: {"id":"foo", "name":"bar", "mails":[{"$ref":"mail","$id":"abc"},{"$ref":"mail","$id":"def"}]}
This means that if you need consistency between the person and the mail collections you should use #DBRef, in this case its behaviour is like a #OneToMany annotation in JPA context.
But if you need a relation between two documents in the 99% of the cases you need a relational dbms instead of mongo.
Related
I found similar questions, but they did not answer my question.
I have two entities with a many-to-one relationship - unidirectional.
But most importantly, the relationship is lazy. Because it is correct to use a lazy connection, everyone knows it.
Code:
#Entity
public class User implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
#Entity
public class City implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
}
interface BaseEntity {
void setId(Long id);
Long getId();
}
I wrote a method that allows you to search by the transferred fields of the entity.
An example of how this works:
public class Search<T extends BaseEntity> {
public List<T> getByFields(T entity, List<FieldHolder> data) {
// create criteria with passed field name and value by reflection
}
}
class FieldHolder {
private String fieldName;
private Object value;
/**
* "true" - means that the field "value" contains id related object
* "false" - elementary type like: String, Wrapper, Primitive
*/
private boolean isRelationId;
}
The problem is that problems start when you need to search and related objects - by creating related queries.
The following entry is used to send the associated field: "city.id" and the problem is that when I transfer the essence of the related object (City) it is in a proxy and I cannot get id by reflection from City.
My function works perfectly if you specify:
#ManyToOne(fetch = FetchType.EAGER)
private City city;
But it will greatly affect performance, since I have a lot of related objects. Therefore, I want to solve this problem for a lazy load.
I know that this is not an easy task. But perhaps there is some opportunity to somehow get around this problem.
I want to save an employee object in couchbase using spring boot java. I am using reactive couchbase driver. My requirement is to save the employee object with employeeId suffixed with hard coded string "-EMPLOYEETYPE".
Example:
Object to couchbase from Java Application:
{ "employeeId" : "12345", "lname" :"ltest", "fname" : "ftest"}
While saving to couch base, key supposed to be generated like
"12345-EMPLOYEETYPE"
Below code is not working, kindly guide me how to achieve it.
Note: I am using lombok so there are no getters and setters.
#Document
public final class Employee {
#Id #GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES,delimiter="-EMPLOYEETYPE")
private String id;
#IdAttribute
private String employeeId;
}
Found a solution. We need to create an instance variable with suffix string literal assigned to it, and annotate with #IdSuffix. (For prefix, #IdPrefix). This field will not be persisted into couchbase and only used to generate id for document.
#Document
public final class Employee {
#Id #GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES,delimiter="-")
private String id;
#IdAttribute
private String employeeId;
#IdSuffix
private String suffix = "EMPLOYEETYPE";
}
Reference Doc: https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.autokeygeneration.configuration
I am working on a Hibernate issue, which involves 2 separate Entity beans defined separately in their own classes:
Store
StoreServer
Note that a Store will have more than one StoreServer - hence the use of the #OneToMany annotation. Please see the code snippets as follows:
Store:
#Entity
#Table(name="Store")
public class Store implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = 5644190852867691168L;
#Id
#Column(name="STORE_NO", nullable=false)
private int storeNumber;
#Column(name="STORE_NAME", nullable=false)
private String storeName;
#Column(name="STORE_PHONE", nullable=false)
private String storePhone;
//other Store fields...
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name="STORE_NO", insertable=false, updatable=false)
private List<StoreServer> storeServers = new ArrayList<StoreServer>();
//getters and setters
StoreServer:
#Entity
#Table(name="Store_Server")
public class StoreServer implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -5410564578856243437L;
#Id
private StoreServerPK storeServerPK;
#Column(name="IP_ADDRESS", nullable=true)
private String ipAddress;
//other StoreServer fields...getters and setters
Since StoreServer has a composite Primary Key, here is StoreServerPK:
#Embeddable
public class StoreServerPK implements Serializable {
/**
* Serializable class - generated UID
*/
private static final long serialVersionUID = -1401889029390423604L;
#Column(name="STORE_NO", nullable=false)
protected int storeNumber;
#Column(name="SERVER_NO", nullable=false)
protected String serverNumber;
//getters and setters
At present, I am getting the correct results, but the performance is unacceptably SLOW. I have switched on logging in Hibernate and I can see that a separate SELECT query is being run for each Store Entity in order to obtain the associated StoreServer records.
Currently, in the logs, I see a single SELECT statement to obtain the Store records (more than 200 results returned). Then for each store, a new SELECT statement to get the StoreServer records. My question is...Why is Hibernate not doing a join (running one query)?
Please could I get some help on how to tell Hibernate to run a single query, using a JOIN?
Thank you
It is called N+1 problem
The solution actually depends on how do you make your query - in case if you are using Criteria API you should use Root.fetch method:
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Store> cq = qb.createQuery(Store.class);
Root<Store> root = cq.from(Store.class);
root.fetch(App_.storeServers, JoinType.LEFT);
cq.select(root);
return em.createQuery(cq).getResultList();
If you are using HQL you should use fetch keyword:
select distinct st from Store st left join fetch st.storeServers
It might be a good idea to validate the number of queries generated by Hibernate in your unit tests using in-memory database like H2 and JDBC Sniffer
I am getting started with Hibernate Search/Lucene using Spring Boot and Spring Data, but I am having an issue with the index not getting updated (Checked with Luke tool).
I have 3 classes in my domain. This is Datasheet, my root entity:
#Entity
#Indexed
public class Datasheet
{
#Id
#GeneratedValue()
private long m_id;
#Field(name="name")
private String m_name;
#Field(name="description")
private String m_description;
#IndexedEmbedded(prefix = "documents.")
#OneToMany(cascade = CascadeType.REMOVE)
private Set<DatasheetDocument> m_documents;
}
Then DatasheetDocument:
#Entity
public class DatasheetDocument
{
#Id
#GeneratedValue()
private long m_id;
private String m_originalFileName;
#Field(name="componentName")
private String m_componentName;
#IndexedEmbedded(prefix = "manufacturer.")
#ManyToOne
private Manufacturer m_manufacturer;
}
And finally Manufacturer:
#Entity
public class Manufacturer
{
#Id
#GeneratedValue()
private long m_id;
#Field(name="name", analyze = Analyze.NO)
private String m_name;
private String m_website;
}
When I explicitly call startAndWait() on the indexer (org.hibernate.search.MassIndexer), then everything is as expected in the index. It contains the fields name, description, documents.componentName and documents.manufacturer.name.
However, when I now do updates through my #RestController classes that call into Spring Data CrudRepository classes, the index only changes when changing a direct field of Datasheet (E.g. name or description). Changing something to the DatasheetDocument instances does not update the index. Any idea why this might be?
Note that I have tried to add backreferences to the parent. For DatasheetDocument:
#ManyToOne
#ContainedIn
private Datasheet m_datasheet;
And for Manufacturer:
#ManyToMany
#ContainedIn
private Set<DatasheetDocument> m_datasheetDocuments;
But that does not help.
I am using Spring boot 1.0.1 which includes Hibernate 4.3.1. I added Hibernate Search 4.5.1. I see that Lucense 3.6.2 gets added transitively as well.
You need the back references for sure. Without them and in particular without #ContainedIn there is no way for Search to know that it has to update the Datasheet index when the DatasheetDocument instance changes.
Have you added mappedBy to the one to many side?
#OneToMany(cascade = CascadeType.REMOVE, mappedBy="m_datasheet")
private Set<DatasheetDocument> m_documents;
Also, how to you update DatasheetDocument? Can you show the code? Either way, you will need to make the associations bi-directional to start with.
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.openSession()
Object customer = fullTextSession.load( Datasheet.class, datasheetDocument.getDatasheet.getId() );
fullTextSession.index(customer);
fullTextSession.flushIndex();
It took me hours to realize, what the problem is:
I have a Spring Rest service and a GET-Method which returns a user in JSON-Format.
The data comes from my database over sessionFactory.
After debugging it turned out, that the Problem is related to my bidrectional onetomany-manytoone relationship.
So calling
User user = (User) sessionFactory.getCurrentSession().load(User.class, userId);
returns a User-Object where user.getCity().getSupplier() runs into an com.sun.jdi.InvocationException. Therefore Jackson is obviously unable to serialize.
But what causes this exception?
#Entity
#Table(name = "T_CITY")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id;
#OneToMany(mappedBy = "city", cascade=CascadeType.ALL)
private Set<User> user;
#OneToMany(mappedBy = "city", cascade=CascadeType.ALL)
private Set<Supplier> supplier;
User:
#Entity
#Table(name = "T_USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
public User() {
}
#Id
private long id;
#ManyToOne
private City city;
Supplier:
#Entity
#Table(name = "T_SUPPLIER")
public class Supplier implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id;
#ManyToOne
private City city;
As mentioned in the other answer, I think you'll find that your issues are related to the x-to-x relationships. This can sometimes create circular reference issues when trying to jsonify the entity beans.
Sometimes you can avoid or get past this by using annotations, other times a wrapper class is needed. I often just write a wrapper class to handle my JSON transactionts
There are many many references to this type of issues spanning many languages. Here a few starting points for you to research.
Google related search regarding circular references in entity objects
Json and Java - Circular Reference
Circular Dependencies With Jackson