I'm working on a Hibernate/Spring application to manage some movies.
The class movie has a many to many relationship with the class genre.
Both of these classes have generated id's using the GeneratedValue annotation.
The genre is saved through the movie object by using #Cascade(CascadeType.SAVE_UPDATE)
I have placed a unique constraint on the genre's type attribute (which is it's name; "Fantasy" for example).
What I would like to do now is have Hibernate check if there is already a genre with type "Fantasy" and if there is, use that genre's id instead of trying to insert a new record.
(The latter would obviously throw an error)
Finally what I need is something like select-before-update but more like select-before-save.
Is there such a function in Hibernate?
Some code:
Movie class
#Entity
public class Movie {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#Lob
private String description;
#Temporal(TemporalType.TIME)
private Date releaseDate;
#ManyToMany
#Cascade(CascadeType.SAVE_UPDATE)
private Set<Genre> genres = new HashSet<Genre>();
.... //other methods
Genre class
#Entity
public class Genre {
#Column(unique=true)
private String type;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id
....//other methods
You may be over-thinking this. Any select-before-update/select-before-save option is going to result in 2 DB round trips, the first for the select, and the second for the insert if necessary.
If you know you won't have a lot of genres from the outset, you do have a couple of options for doing this in 1 RT most of the time:
The Hibernate second-level cache can hold many if not all of your Genres, resulting in a simple hashtable lookup (assuming a single node) when you check for existence.
You can assume all of your genres are already existing, use session.load(), and handle the new insert as a result of the row not found exception that gets thrown when you reference a genre that doesn't already exist.
Realistically, though, unless you're talking about a LOT of transactions, a simple pre-query before save/update is not going to kill your performance.
I haven't heard of such a function in Hibernate select-before-update/select-before-save
In situations like these you should treat Hibernate as if it was JDBC.
First if you want to know if you even have such a Genre you should query for it.
if you do. then the SAVE_UPDATE will not create a new one when you add it to a movie.
if you don't, Hibernate will create a new Genre row in the database and add the connection to the many_to_many table for you.
Related
Using Spring Boot JPA, I am doing a self join on a table of "Person" with attributes id, name and parent_id. parent_id is a foreign key referencing Person.id. So, a Person will have zero or one parent. A sample of my domain class is below.
#Entity(name="person")
public class Person {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parent_person_id")
private Person parent;
// constructors, getters, setters, etc
}
This actually works just fine; when I query with CrudRepository.findById() for example, I get a Person object with an embedded Person object (parent), which may have another embedded Person object (grandparent), etc until I get to a Person without a parent.
My question is, how may I retrieve only a Person and their immediate parent without recursing any further (no grandparents, great-grandparents, etc)?
I imagine I could simply avoid the join, and make parent_id a plain #Column, then in the service layer do an additional query to find the parent, but I'm wondering if there is some Jpa magic that could make it easier than that.
Actually, this was not as hard as it seemed. Converting my Person entity to a dto provided me with the opportunity to simply STOP at the parent, and not recurse through the whole tree!
UPDATE
I find something interesting
When I run JPQL like this
SELECT s FROM STUDENTS s WHERE s.community=:community
then this issue happens, but this query runs fine and returns all necessary fields
SELECT s FROM STUDENTS s WHERE s.id=:id
meaning if there is an inner join with its child field, then somehow the value of the other field get missing. I got both native query and both returns all necessary field values. Must be openjpa dismiss the school fields some how when native query returns
Yes, Community and School both may map to another entity, but how come that could cause this strange behavior? I am kind of mad with OpenJPA
I have spent hours to fix this strange issue
there is a class contains several #ManyToOne relation
public class Student{
// Relationships
#NotNull
#ManyToOne(fetch=FetchType.EAGER)
private Teacher teacher;
#ManyToOne(fetch=FetchType.EAGER)
#Column(name = "SCHOOL_ID")
private School school;
#ManyToOne(fetch=FetchType.EAGER)
private Club club;
#ManyToOne(fetch=FetchType.EAGER)
private Bus bus;
....
}
Each of the many side has a definition like this(Only give the School entity as it has the issue):
public class School implements Institution{
// Relationships
#OneToMany(mappedBy="school")
private List<Student> students= new ArrayList<Student>();
....
}
The problem here is when I try to get All students for a certain age:
SELECT s FROM STUDENTS s WHERE s.age=:age
I can clearly see the all other #ManyToOne fields' value ONLY EXCEPT school, and in Oracle database the school_id field clearly stored with correct data which map to the correct entry in School table
So what could be the possible reason for this situation?
I am using Spring MVC3 + Openjpa + Roo
Your school mapping should use a #JoinColumn annotation (not a #Column annotation):
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "SCHOOL_ID")
private School school;
Okay. I am getting a little confused here...
Lets say I have a class called User.
class User {
// all variables
// all getters and setters
}
Now, I use JSR 303 validation and put #NotNull, #Range, etc here for the variables.
I use this as Form / Command object. Meaning, when a form a submitted, the values are validated and BindingResult gives me errors.
Should this be used as in Entity Object for Hibernate as well? (If so, I need to add other Hibernate annotations like #Entity, #Id, #Column, etc on top of Validation annotations)
When we load the data from Database, do these validations kick in as well? (If yes, what if the data is already existing, and do not confirm to the validations?)
Where do we normally write business validations, like for example, country exists or not in the database, xyz value exists in a different table, etc?
Questions arise here as well:
- User form may not have all the fields that exist in the User class
- Database table User may have more fields or less fields than User class
- Form may have fields from different objects as well, say User and and Order.
How do we handle these?
Trying to wrap my mind around it
No you shouldn't mix entities objects and values objects. Entities objects are for DB mapping and values objects are used in the presentation layer.
To validate an object annoted, you need to use a Validator (commonly used with a DataBinder. Spring validation)
Is it DB constraints or backend validation?
For your last question, that's one of the reason to have 2 differentes objects for your presentation layer and your persistence layer. This way values objects can match what is displayed or input by the user (Form).
Entity object is an object of our plain old java class(POJO) Model/Business class, which needs to be persisted in a database using Hibernate, while Value Type object is an object of another class but it is stored as a part of Entity object within a database table.
#Embeddable
#Data
#AllArgsConstructor
public class Address{
private String country;
private String city;
private String street1;
private String street2;
private postalCode;
}
#Entity
#Data
#AllArgsConstructor
public class Person{
#Id
private Long id;
private String firstName;
private String lastName;
private ing age;
private Address address;
}
}
So after running this code in spring and hibernate project you will see Person table in database is created with it's attributes and Address class attributes.
For more information, I suggest reading this:
[https://www.decodejava.com/hibernate-value-type-object.htm][1]
I have some problem with Morphia.
Could someone help me?
I am writing web-project on Spring + MongoDB about movies and celebrities.
I have entity class Genre:
#Entity(value="genres")
public class Genre implements IGenre {
#Id
#Indexed
private ObjectId id;
#Indexed
private String name;
private String description;
private long quantity;
private Set <IMovie> movies;
//getters and setters
}
And entity class Movie:
#Entity(value="movies")
public class Movie implements IMovie {
#Id
#Indexed
private ObjectId id;
#Indexed
private String originalTitle;
private String year;
private Set <IGenre> genres;
// getters and setters
}
I have 30 genres. And for example one of them: Comedy.
Also I have 250 000 comedies.
And now I want to do movie pagination by genre = comedy.
How I can get only 20 records from all comedies.
If I use #Embedded or #Reference annotation I will still get the entire list at once. And it's to big for use it in controllers.
You should change your data schema for doing such a query. The schema you use has a circular dependency, in your Genre entity you are holding Movies entity and in movies you hold Genre. Also holding all of the movies according to genre is not easy to query. If I were you I would use such a schema.
#Entity(noClassnameStored = true) // you wouldn't have any problem when you change the class name or package of your class if you don't store the classname through this annotation
public class Movie implements IMovie {
#Id
#Indexed
private ObjectId id;
#Indexed
private String originalTitle;
private String year;
private Set <String> genres; // unique identifier of the genres instead of embedding the whole genre entity
// getters and setters
}
So having such a schema, you can retrieve the movies having a particular genre by writing a simple $in query through genres field. Example query for your case:
datastore.find(Movie.class).field("genres").in(Lists.newArrayList("comedy")).limit(20).asList;
In the below web-page of mongo you can find suggestions about how to design your schema according to diffrerent scenarios.
http://docs.mongodb.org/manual/core/data-modeling/#data-modeling-patterns-and-examples
I'm not too familiar with Mongo, but it looks like you would need to implement a custom query here. Whatever you do, you need to pass a start for your page, as well as a page size (20 in your case).
You can do pagination in Morphia by combining .offset(page_start) and .limit(page_size) on a query. So first you would create a query to get movies that belong to a certain genre, and then apply the pagination.
Looks like it is handled using repositories in Spring http://static.springsource.org/spring-data/data-mongodb/docs/1.0.0.RELEASE/reference/html/#repositories.special-parameters
You'd use a Pageable implementation to pass paging data, without worrying about doing the offset and limit calls yourself. There's an example of "Web pagination" further in the doc.
Hope this helps!
Using spring-data-neo4j, I want to create two classes using #RelationshipEntity(type="OWNS") to link a Person class to both a Pet and Car.
#RelationshipEntity(type="OWNS")
public class OwnsCar {
#Indexed
private String name;
#StartNode
private Person person;
#EndNode
private Car car;
}
#RelationshipEntity(type="OWNS")
public class OwnsPet {
#Indexed
private String name;
#EndNode
private Person person;
#StartNode
private Pet pet;
}
This saves to the Graph Database properly with no problems, as I can query the actual Node and Relationship and see they type, etc.
But when I attempt to use #RelatedTo(type="OWNS", elementClass=Pet.class) I either get a class cast exception, or when using lazy-initialization I get incorrect results.
#NodeEntity
public class Person {
#Indexed
private String name;
#RelatedTo(type="OWNS", direction=Direction.OUTGOING, elementClass=Pet.class)
private Set<Pet> pets;
#RelatedTo(type="OWNS", direction=Direction.OUTGOING, elementClass=Car.class)
private Set<Car> cars;
}
The result I get when I attempt to print our my person(my toString() has been omitted, but it simply calls the toString() for each field) is this:
Person [nodeId=1, name=Nick, pets=[Car [nodeId=3, name=Thunderbird]], cars=[Car [nodeId=3, name=Thunderbird]]]
Does anyone know if this can be done, should be done, is just a bug or a feature that is missing?
It seems like the problem is, that the annotation causes springDataNeo4j to priorize the relationship name. I tried the same on another sample I created. If both annotations contain
type="OWNS" it mixes both 'objects'. When I omit this information, and only use direction and type, it works for me.
Unfortunately this will lead to a problem if you are using another #RelatedTo annotation with more Pets or Cars related with another annotation. As it would not differ between "OWNS" and any other relation to a Pet-Type, the set returns all related pets (example: peter ->(HATES-Relationsip)->dogs).
If it's a bug or not, I can't tell... But for the database: There are only nodes and relations. Both are not typed, so neo4j does not know anything about your 'Pet'- or 'Car'-Class. Spring data neo4j handles this, either by indexing all nodes per type and setting a type-attribute, or using a specific graph-layout (with subreferences). Even if you would want to fetch all pets of a person with a traversal description, you would have so much more code to write, since the outgoing relations with name 'OWNS' contains two types of objects.
I would recommend using two different names. It's easier to write your custom traversals/queries later on, and its probably even faster as well, because no class-type comparison will be needed. Is there any reason, why you would need these specific names?
PS: It is possible, that not everything is 100% accurate. I don't know springdataneo4j in detail, but that's what I figured out so far.