I have these 2 tables
Users(
id PK,
name VARCHAR(30)
);
The other table is
Orders(
id PK,
orderBy FK Users.id,
orderTo FK Users.id
);
Now, what I want to do is to create Orders entity class which maps orderBy and orderTo to the user. But the most thing i am confuse about is what cascading i should use.
class Orders{
///
#ManyToOne(fetch = FetchType.Lazy
#JoinColumn(name="orderBy")
Users orderBy;
///
#ManyToOne(fetch = FetchType.Lazy
#JoinColumn(name="orderTo")
Users orderTo;
}
I am thinking to create two fields in Users Table such that
class Account{
///
#OneToMany(fetch = FetchType.Lazy)
#JoinColumn(name="orderTo")
List<Orders> ordersReceived;
///
#OneToMany(fetch = FetchType.Lazy)
#JoinColumn(name="orderBo")
List<Orders> ordersPlaced;
}
But again, I am not sure what cascading shall i use. My Users table will be populated by some other processes so orders has nothing to do with. I don't want when i am placing an order, that particular transaction should add/delete anything. HOWEVER, i might need to update a specific field of User whenever i place an order.
I'll suggest to avoid to use cascade at all (if possible)... When you place an order, you should follow the following steps:
1) load your user from your database
2) create your order ...
3) linkup your order to your user (this is, order.setOrderBy(user))
4) persist your order with your EntityManager.
5) Change your user attribute.
From my experience, Cascade should be used carefully. I only used it for persist entities in one shoot (Cascade.PERSIST) (example: persisting a newly user with another new entities like orders)
Related
I am using spring data jpa
I have two tables person, order
Both have relationship like one to many from person to order
order(person_id) -> person(id)
create table person (id int primary key, name varchar(20));
create table order (id int primary key, name varchar(20), person_id foreign key references person(id));
Consider i have schema in place for both tables and there is no data yet.
I need to insert this data -
Person {name: "person1"}
Order {name: "order1", person_id: <corresponding to person1 record>}
Does inserting the data related by foreign key needs two calls to db?
saving the person
take the primary key from the saved person entity (step 1) then save Order?
Person person = new Person("person1");
Person person = personRepository.save(person);
Order order = new Order("order1");
order.setPersonId(person.getId());
orderRepository.save(order);
Or is there any alternative to save data to two tables using single call to db?
Retrieving the data
If i need to retrieve the person along with orders, will spring data jpa give the result in single db call or need to extract data from two tables separately?
Person person = personRepository.findByName("person1").get(); \\for eg: consider name is unique here
List<Order> orders = orderRepository.findByPersonId(person.getId());
or any alternative in single db call?
Giving clarity to these questions is really appreciated.
Thanks for the answers in advance.
Have a look at cascade types. By adding a cascade type, you could save both objects with just 1 repository call.
#Entity
public class Order {
// other fields...
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Order> orders;
}
#Entity
public class Person {
// other fields...
#ManyToOne
private Person person;
}
Order order = new Order();
Person person = new Person();
person.setOrders(List.of(order));
order.setPerson(person);
personRepository.save(person); // <--- since save action on person cascades, it will also save the order.
Make sure that the objects are linked to each other before saving (the 2 rows above the repository call in the example above)
Regarding fetching data
If you call e.g. the personRepository and it has orders linked to it, you can either access them by configuring eager fetch (not recommended) or by wrapping your method in a Transactional annotation and access the orders programmatically instead.
#Transactional
public void doSomething() {
Person person = personRepository.findById(1);
List<Order> orders = person.getOrders();
}
Note that from a db perspective, in both scenarios with saving and fetching data, the same number of queries will be executed as when calling with separate repositories, but you reduce the code needed to do so and its a bit easier to work inside a transaction and only focus on the java object instead of having to call multiple repositories, especially as your db data model grows.
I have three tables with simple structure:
pub [id, name]
days [id, name]
pub_days [id, pub_id, days_id]
For some unholy reason, somebody thought that compound identity for pub_days table (that would be pub_id + days_id) is not enough and added own primary key. I can't change it now, other and larger system depends on that. #sigh
I am trying to map this to Hibernate with standard #ManyToMany JPA annotation like so (I omitted getters, setters, #Entitiy annotations and other clutter):
class Pub {
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "pub_days",
joinColumns = {#JoinColumn(name = "pub_id")},
inverseJoinColumns = {#JoinColumn(name = "days_id")})
#OrderBy("id")
private List<Day> pubOpeningDays;
}
class Day {
#Id Long id;
String name.
}
when I execute following code:
Day day = repository.find(Day.class, id);
pub.getPubOpeningDays().add(day);
repository.persist(pub);
I get this error:
ERROR: ORA-01400: cannot insert NULL into ("PUB"."pub_days"."id")
Sadly, that makes perfect sense, because I haven't mapped that ID anywhere. The thing is, I don't even want to. I want it to be generated, but not sure how do I overcome this issue with #ManyToMany mapping. Any ideas?
What you can do is like I mentioned in my comments you can create a separate entity CD which will in turn connect with two classes A and B, Now relationship would be many to many between A and B, and hence A (many to many) CD (many to many) B. Now as per your requirement whenever you need to fetch the instance of A or B, what you can do is simply fire a query in the DB with proper parameters i.e id of a or id of b this will help you get your required result.
I only see two choices, either you change your mapping to a list of PubDay as samwise-gamgee told you in the comments or you add a trigger on insert on table pub_days which set a value for the column id if it is null (it could be using a sequence). But this approach depends on the features supported by your DB.
The scenario is this. I have two entities, User and Post for a Twitter-like application. Consider the following
User Alice creates a post.
User Bob favorites the aforementioned post.
User Alice tries to delete the post.
When User Alice tries to delete the post, I get an error from Hibernate;
ERROR: update or delete on table "posts" violates foreign key constraint "fk_jof9iwt9m3lfjxix5ejri4iv9" on table "favorite_posts"
Detail: Key (id)=(16) is still referenced from table "favorite_posts".
Code for my entities;
#Table(name = "users")
public class User {
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> posts = new ArrayList<Post>();
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "favorite_posts")
private Set<Post> favoritePosts = new HashSet<Post>();
...
}
#Table(name = "posts")
public class Post {
...
#ManyToOne
private User user;
...
}
My alternatives as I see them;
Fetch all of the Users who have favorited a Post, and then clear all of them one by one.
Make the association bidirectional and then clear all of the favorites before deletion.
How can I get Hibernate to delete the association (= the corresponding row in favorite_posts) before trying to delete the entity?
I'd solve this problem at the database level using ON DELETE CASCADE on the foreign key. It's the simpliest solution, and it doesn't bring extra complexity to the application layer.
However, this approach doesn't play well with your domain model, because your model treats User - Post relationships (both post and favoritePosts) as parts of a User. Therefore, you'll get constraint violation if you try to save a User while one of the Posts it's associated with is being removed.
This can be solved by moving ownership of User - Posts relationships away from User, for example, as follows:
User <- Post: you probably would never need all Posts created by a User at once (without filtering or pagination). Therefore it makes no sense to map a relationship from User to Post, unidirectional relationship from Post to User is enough.
User <- FavoritePost -> Post: moving "favorite post" relationship to its own entity allows it to have its own lifecycle. Now FavoritePost can silently disappear when associated Post is removed, and it won't create any inconsistency at the application level.
Could somebody help me in setting appropriate annotation in hibernate for following case:
I have three tables:
Account, Card and AccountCard.
AccountCard is joining table for OneToMany relationship between Card and Account (account has many cards, card is attached to only one account).
I need to add to Account a List cards property and to Card model Account account property. This is the easy thing.
The problem is that I get "Cannot insert null value to AccountCard.id" while persisting Account with Cards.
Also I need to use sequence to generate IDs for joining table but don't know how.
Any help would be very appreciated.
Here is the code in Card:
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "account_card", joinColumns = #JoinColumn(name = "crd_id"), inverseJoinColumns = #JoinColumn(name = "acc_id"))
private Account account;
I don't want to have a mapping in Account class so List cards is not added.
In your #JoinTable annotation, I see reference to an account_name table and not AccountCard. Is there actually an AccountCard table somewhere?
A join table usually doesn't need an id key of its own, and if you have hibernate autogenerate your table DDL it won't include one.
If you do indeed need an id on the join table, I don't think there's a way or a need to make hibernate aware of it, but you should make the column NOT NULL AUTO_INCREMENT in your SQL DDL.
Did you try to generate the tables first in the database (in my case mysql) and then create the entity with an ide like nebans? An auto increment id column in mysql then ends with:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
Check out http://netbeans.org/kb/docs/javaee/ecommerce/entity-session.html for a sample how to use netbeans to create entities from database
I have the following existing DB schema, which I'd like to recreate with Java and plain JPA annotations (using hibernate as provider, so hibernate specific annotations would work as a last resort):
CREATE TABLE users (
user_id NUMBER NOT NULL -- pk
);
CREATE TABLE userdata_keys (
userdata_key_id NUMBER NOT NULL, -- pk
key VARCHAR2(128) NOT NULL
);
CREATE TABLE users_userdata (
user_id NUMBER NOT NULL, -- fk users.user_id
userdata_key_id NUMBER NOT NULL, -- fk userdata_keys.userdata_key_id
value VARCHAR2(256)
);
I've thus created the following classes and annotations:
class User {
#Id
Long id;
#OneToMany
Set<Userdata> userdata;
}
class UserdataKey {
#Id
Long id;
String key;
}
class Userdata {
String value;
#EmbeddedId
UserdataId userdataId;
}
#Embeddable
class UserdataId {
User user;
UserdataKey userdataKey;
}
I left out columnName attributes and other attributes of the entities here.
It does however not quite work as intended. If I do not specify a mappedBy attribute for User.userdata, hibernate will automatically create a table USERS_USERS_USERDATA, but as far as I've seen does not use it. It does however use the table which I specified for the Userdata class.
Since I'm rather new to Java and hibernate as well, all I do to test this currently is looking at the DB schema hibernate creates when persisting a few sample entries.
As a result, I'm entirely puzzled as to whether I'm doing this the right way at all. I read the hibernate documentation and quite a bunch of Google results, but none of them seemed to deal with what I want to do (composite key with "subclasses" with their own primary key).
The mappedBy attribute is mandatory at one of the sides of every bidirectional association. When the association is a one-to-many, the mappedBy attribute is placed ot the one- side (i.e. on the User's userdata field in your case).
That's because when an association is bidirectional, one side of the association is always the inverse of the other, so there's no need to tell twice to Hibernate how the association is mapped (i.e. which join column or join table to use).
If you're ready to recreate the schema, I would do it right (and easier), and use a surrogate auto-generated key in users_userdata rather than a composite one. This will be much easier to handle, in all the layers of your application.