"Upgrade" entity from base class to child - java

I have 2 entities where one is derived from another
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private String login;
#Column(name = "password_hash",length = 60)
private String password;
}
and
#Entity
#Table(name = "bidder")
public class Bidder extends User {
#Column(length = 100, unique = true)
private String email;
}
When Bidder object is persisted, in both user and bidder tables created a record which have the same id value.
Initially for each system user - we create User entity, but after some time User needs to be "upgraded" to Bidder.
Is there a way I can "upgrade" User to Bidder in some elegant way?
I have several ideas -
insert a record in Bidder table with the same id, that User has
initially create Bidder entity object, but the thing is not all
users can be Bidders
create BidderInfo entity class which will have
One-to-One relatioship to User
don't create Bidder child class, but
add all the needful fields from Bidder to User which will convert
User to Bidder initially, and which is spoils the semantics of User
all the above ideas don't look like perfect solution.

If you don't really want to have 2 tables you can use InheritanceType.SINGLE_TABLE and a #disciminatorValue.
With this way, to change the type of your user you just need to change the discriminator value.
You'll find an example of "single table strategy" to this page.
This way seems to be the easiest, but it may not the best.

Related

OOPS DataBase Design help for Bug tracking application

lets say there are two tables TICKET and USER
table USER(username, password, roles)
Table TICKET(ticketname,users_assigned)
the problem in the TICKET table is how can I have attribute List in table TICKET. can someone guide me on how to make the TICKET table. coz I'm planning to implement the Ticket table with List as a property using java Spring Data JPA ORM.but I don't know how to create tables that go with it
I was thinking maybe have another table TicketUser(username,ticketname). I just want to know if there's a better way to design this. thanks in advance.
Your thinking is correct.
First Step : add an id column to all you tables
second step : create a table TicketUser (userId, ticketId) referencing the respective foreign keys.
Or you can also let jpa create the table for you you just create classes in you code like
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id; // or long
private String name, password, roles;
#OneToMany
private List<Ticket> ticketList;
// constructor, getters, setters, etc.
}
For Ticket class
#Entity
public class Ticket {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id; // or long
private name;
#ManyToOne
private User user;
//Constructor, Getter and Setters, ...
}

Foreign key in hibernate, why create objects, how to update a record in referenced entity

I'm new to Hibernate environment. I have a basic question and I'm still trying to understand why people are picking Hibernate over SQL.
I have two tables, lets say one is user, one is a book.
A user can has many books, but a book only has a one owner.
If I used SQL, I try to write at
Table User,
int uid
PRIMARY KEY (uid)
Table Book,
int bid
int oid //ownerid
PRIMARY KEY (bid)
FOREIGN KEY (oid) REFERENCES User(uid)
I couldn't do this in Hibernate. I've tried the following:
Generated a user table without any relation, only #Id annotation for uid.
#Entity
#Table(name="USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "uid")
private Long uid;
public Long getUid()
{ return uid;}
}
Generated a book table, but I cannot understand this point. Everything on the internet says that I need to use #ManyToOne and #JoinColumns. If I use them I need to have an object:
#Entity
#Table(name = "BOOK")
public class Book{
#ManyToOne
#JoinColumn(name="uid",
referencedColumnName = "What should I write here?")
#Column(name ="oid") //owner id
private User user;
}
1- Why would I need to create a user object for my uid in book table? I just want to store a userid in my books table with Foreign Key constraint (If the user does not exist, then the book cannot be exist either)
2- Also, if I want to reach the userid of the owner, should I use this:
public Long getOwnerId()
{
return user.getUid();
}
3- If I want to change the owner of the book, what should I do? Since there is a whole user member in my book object, how can I update only the ownerid in my book table?
Why would I need to create a user object for my uid in book table?
You don't need to change anything to your database table. Hibernate will read the oid from the book table, and will populate the Book.user field with the object of type User identified by this oid.
That way, when you display the information about a book in your application for example, and you want the name of the owner to be displayed, all you need to do is
display(book.getUser().getName());
Instead of having to get the ID of the user, then execute a second database query to get the user.
if I want to reach the userid of the owner, should I use this:
yes. Or you don't ad any method, because any caller is able to do book.getUser().getUid() by itself.
If I want to change the owner of the book, what should I do?
Assuming you have the uid of the new owner, you would get the user identified by this ID, and set in on the book:
User newOwner = entityManager.find(User.class, newOwnerId);
// or User newOwner = entityManager.getReference(User.class, newOwnerId);
// depending on what you prefer
book.setUser(newOwner);
Regarding your mapping of the ManyToOne, it's wrong. You need to read the Hibernate manual, and the javadoc, instead of trying random things. It should simply be
#ManyToOne
#JoinColumn(name = "oid")
private User user;
In a ManyToOne Entity you just need to specify the name of yourforeign key inside the #JoinColumn annotation, like this:
#Entity
#Table(name = "BOOK")
public class Book{
//private Long bid;
#ManyToOne
#JoinColumn(name="oid")
private User user;
}
The referencedColumnName paramether is most used for ManyToMany relationships.
Hibernate is a object relational mapper to represent the table based data structure of a relational database in a usual object oriented way, nothing more or less.
You dont have to use annotations like #ManyToOne, #OneToMany, etc. You can just do this:
#Entity
#Table(name="USER")
public class User {
#Id
#Column(name = "uid")
private Long uid;
// setters and getters
}
#Entity
#Table(name = "BOOK")
public class Book {
#Id
#Column(name = "bid")
private Long bid;
#Column(name = "oid")
private Long oid;
// setters and getters
}
EntityManager em;
Long uid = em.createQuery("SELECT u.uid FROM User u WHERE ....", Long.class).getSingleResult();
book.setUid(uid);
Most developers dont want to handle with that much withs IDs and database access etc. They just want to access their business model in a natural object oriented way and there hibernate or JPA comes in:
EntityManager em;
User user1 = em.find(User.class, 1L);
Book book1 = new Book();
book1.setUser(user1);
You dont have to create a User instance for every single Book instance. Just reuse the existing User instance.
In your Example i would do the following:
#Entity
#Table(name="USER")
public class User {
// ...
#OneToMany(mappedBy = "user")
private Collection<Book> books = new ArrayList<>();
public void add(Book book) {
book.setUser(this);
books.add(book);
}
public void remove(Book book) {
book.setUser(null);
books.remove(book);
}
}
#Entity
#Table(name = "BOOK")
public class Book {
// ...
#ManyToOne
#JoinColumn(name="oid", referencedColumnName = "uid")
private User user;
}
EntityManager em;
User user1 = em.find(User.class, 1L);
Book book1 = new Book();
Book book2 = new Book();
user1.addBook(book1);
user1.addBook(book2);
book1.getUser(); // user1
book2.getUser(); // user1
Yes, if u want to handle with bare IDs
Set another User via setter. The corresponding uid in the book table will be updated.
book1.setUser(user2);
will result in the statement
update BOOK set oid = 2 where bid = 1;

Separate Table vs Extra Columns in JPA/Hibernate

I am wondering about best practices in database design with Hibernate.
I have a User entity that is going to have a lot of different settings. For each set of settings, I have to either add them as extra columns in the User table or create a separate entity and connect them with a #OneToOne relationship. It is my understanding that #OneToMany and #ManyToOne relationships should generally take place in separate tables because you should not have columns that are optional.
But it is kind of unclear for #OneToOne relationships. I think there is a case for using #OneToOne because ORMs will select all single attributes by default and having a lot of columns will slow down that process.
An example of what I am talking about can be illustrated by
#Entity
public class User{
#OneToOne
private ForumSettings forumSettings;
#OneToOne
private AccountSettings accountSettings;
#OneToOne
private SecuritySettings securitySettings;
}
vs
#Entity
public class User{
#Column
private boolean showNSFWContent; //Forum Setting
#Column
private int numberOfCommentsPerPage; //Forum Setting
#Column
private boolean subscribedToNewsLetter; //Account Setting
#Column
private boolean isAccountBanned; //Account Setting
#Column
private boolean isTwoFactorAuthenticationEnabled; //Security Setting
#Column
private boolean alertForSuspiciousLogin; //Security Setting
}
The above is a simple example to show the concept, but in practice there would be many more columns in the 2nd portion.
I know that this might be opinion based, but I am hoping someone could share the pros/cons of both choices.
Thank you very much
Your question is in general about Data normalization. Normalization is itself extensive field of study and basically is a way of structuring database tables avoiding redundancy and making sure that updates don’t introduce anomalies.
And first rule of normalization says a table shall contain no repeating groups. In your case it does.
SOLUTION 1 : Store UserSettings as Entity as map as OneToMany relationship
#Entity
public class User
#OneToMany
private List<UserSettings> userSettings;
And then you can query for particular setting type by joining User and UserSettings entities.
For example (JPQL)
SELECT user u
JOIN u.settings us
WHERE us.settings_type = 'account_settings'
and us.settings_value = 'secure' // or any other logic
Advantage of this approach is that UserSettings will have it is own persistence identity and can be queried by it's own. It it is not dependent on parent.
For example :
SELECT q from Query q where ...
Solution 2 : Store settings in a collection of basic elements
You can store User Settings in the collection (Each user will have it's own set of settings)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#ElementCollection
#CollectionTable(name="USER_SETTINGS")
#MapKeyColumn(name="SETTINGS_TYPE")
#Column(name="SETTINGS_VALUE")
Map<String, Boolean> userSettings = new HashMap<>();
UserSettings collection will be stored in a separate table with foreign key to User table. UserSettings does not have it is own persistence ID, is dependent on User entity and can be queried only through it is parent ('User')
Solution 3: Store User Settings as Embedded type
Embedded type is not an entity, it does not have it is own persistence ID and is depends on parent type, stored as part of parent record in database (in User table)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#Embedded
private UserSettings userSettings;
UserSettings is in separate class, but stored in User table.
#Embeddable
public class UserSettings {
private List<String> securitySettings; // or any other collection type
private List<Boolean> forumSettings;

How should I map these 2 simple entities in JPA?

What I currently have:
#Entity
public class Payment {
#Id #GeneratedValue
private long id;
#Column(unique = true)
private Date period; // Only used for year and month
...
}
#Entity
public class Department {
#Id #GeneratedValue
private long id;
...
}
The Payment entity just holds default payments that need to be paid by all departments only once pear year and month. There is no relationship needed for between them as all Departments pay all Payments.
What I want to achieve:
I want to distinguish between the currently shared payments and some other Department specific payments. So a Department will be able to choose to use all the shared payments (as it is currently designed) or define its own payments and not use any of the other ones.
The company Payments should keep working in the same way and I have to make sure that the Department payments are unique for each department too.
In OOP terms, I think I need to model any of the following options:
Probably the first one would be more appropriate.
Note I can't change the way any entity is currently identified. However, I can add uniqueness on any other fields.
Questions:
What would be the appropriate way to do this in JPA2?
Is a Payment hierarchy the way to go? How should it be mapped to make sure the unique
fields don't collide?
Is there any way to avoid the hierarchy?
I think the scenario does require a relationship:
#Entity
public class Payment {
#Id #GeneratedValue
private long id;
#Column(unique = true)
private Date period; // Only used for year and month
#ManyToOne
private Department department;
}
This would allow any type of payment to be created for any department. As far as default payments for a department, I think that is outside the responsibility of the ORM and should be handled within the business logic.
If I understood you correctly, you need to achieve uniqueness per department. It's possible using compound id.
Some points:
if you want to use compound keys(period+department_id) you have to set them both and your default payments should have 1 common fake Department to which all default payments will belong to.
In general case I would follow Kevin's approach. It's easy and less error-prone. Anyway you decide.
#Entity
public class Payment implements Serializable {
#EmbeddedId
private Period period;
}
#Embeddable
public class Period implements Serializable {
#ManyToOne
private Department department;
private Date period;
}
#Entity
public class Department implements Serializable {
#Id#GeneratedValue
private long id;
#OneToMany
private List<Payment> payments = new ArrayList<Payment>();
}
I haven't been able to keep the PK in my Payment entity and also maintain a unique index on both Period and Department. In DB terms I'm looking for these:
Payment(ID, Period, FK_Department)
And this table should be added a unique index in Period and FK_Department that would allow nulls in FK_Department (as you can see a compound PK is not an option here for that reason and because I need to keep the same PK structure used). With that table, all Payments with null FK_Department value will be the generic/default/company payments while the ones with a non-null FK_Department will be the ones that a particular department has assigned, so it will use those instead of the company ones.
Due to my lack of knowledge of JPA I couldn't manage to replicate this schema. However, I could create a similarly functional schema. This is the best I came up with so far:
With its obviously awful period duplication I can manage to create two unique index for each table: one for the Period of the CompanyPayment entity and one for the Period and Department pair of the DepartmentPayment entity:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Payment {
#Id #GeneratedValue
private long id;
...
}
#Entity
public class CompanyPayment extends Payment {
#Column(unique = true)
public Date period;
...
}
#Entity
#Table(uniqueConstraints =
#UniqueConstraint(columnNames = { "period", "department_id" })
)
public class DepartmentPayment extends Payment {
public Date period;
#ManyToOne(optional = false)
#JoinColumn(name = "department_id")
private Department department;
...
}
I will be using this solution for now but I'm open to any other better solution.

Hibernate entity - add business logic to entity?

I have entity:
#Entity(name = "Term")
#Table(name = "extra_term")
public class Term implements Cloneable, Serializable{
This entity has ID
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
And this entity has attribute with List of users owned by this entity (users are registered to term)
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "etc. etc. etc. })
private List<TermUser> users = new ArrayList<TermUser>();
Is it possible to declare business method to count the sum of this users registered to concrete term? Something like
public long getUsersCount() {
return QUERY TO THE DATABASE "GET COUNT OF USERS REGISTERED TO TERM WITH ID
(where ID is the id of this entity?)"
}
I need to count the number of users registered to term straight from the entitz but I dont want to have a attribute "registeredUsersCount" in DB.
That's exactly what #Transient is for.
Every non static non transient property (field or method depending on the access type) of an entity is considered persistent, unless you annotate it as #Transient.

Categories