I'm making a Spring boot application with Hibernate ORM framework.
I have Employee entity there:
#Entity
public class Employee {
private String firstName;
private String position;
//// more than 30 private fields
//// fields related to one sublogic
private String category;
private LocalDate categoryAssignmentDate;
private LocalDate categoryAssignmentDeadlineDate;
private LocalDate docsSubmitDeadlineDate;
}
There are more than 30 private fields in Employee class.
And as you can see, I have 4 fields related to same sublogic Category.
So my question is: Is it a good practise to split my Employee entity into two entities Employee and Category, which will be connected as OnetoOne relationship?
Does it make the code clearer?
Use embedded and embeddable to prevent double table mapping and unnecessary OneToOne relations.
#Entity
public class Employee {
private String firstName;
private String position;
#Embedded
private Category category
}
#Embeddable
public class Category{
private String category;
private LocalDate categoryAssignmentDate;
private LocalDate categoryAssignmentDeadlineDate;
private LocalDate docsSubmitDeadlineDate;
}
You might need to add attribute overrides
Related
I would like to design a booking system using optaplanner, bellow my business model:
Customers (id, name) //Customer table
Services(id, name, description, duration) //services that a customer can book, duration can be 15min, 30min, ..., N x 15min
Employees(id, name) //Employee tables
Appointment(id, customerId, employeeId, serviceId, startTime, endTime)
To book an appointment, the customer will select:
The day of the appointment (mandatory)
A list of services (mandatory)
A list of employees (optional)
I would like to know I can design the model to return the list of availability for a given day, given list of services.
Bellow a basic pseudo-code model :
#Entity
public class Service extends PanacheEntityBase {
#Id
#GeneratedValue
#NotNull
private Long id;
#NotBlank
private String name;
private int durationInGrains;
}
public class TimeGrain {
public static final int GRAIN_LENGTH_IN_MINUTES = 15;
private int grainIndex; // unique
private int startingMinuteOfDay;
}
#Entity
public class Employee extends PanacheEntityBase {
#PlanningId
#Id
#GeneratedValue
#NotNull
private Long id;
#NotBlank
private String name;
}
#Entity
public class Appointment extends PanacheEntityBase {
#Id
#GeneratedValue
#NotNull
private Long id;
private Employee employee;
private Service service;
private LocalDateTime startTime;
private LocalDateTime endTime;
}
#PlanningEntity
public class Availability {
#PlanningVariable(valueRangeProviderRefs = { "timeGrainRange" })
private TimeGrain startingTimeGrain;
#PlanningVariable(valueRangeProviderRefs = "providerRange")
private Provider provider;
private Service service;
}
#PlanningSolution
public class AppointmentAvailability {
#ValueRangeProvider(id = "timeGrainRange")
#ProblemFactCollectionProperty
private List<TimeGrain> timeGrainList;
#ProblemFactCollectionProperty
#ValueRangeProvider(id = "providerRange")
private List<Provider> providerList;
#ProblemFactCollectionProperty
#ValueRangeProvider(id = "appointmentsRange")
private List<Appointment> appointmentList;
#PlanningEntityCollectionProperty
private List<Availability> availabilityList;
#PlanningScore
private HardMediumSoftScore score;
}
As I am new to optaplanner, could you please advise if this is the way to go?
UPDATE 1: I have simplified the problem to the minimum for design purposes.
Take a look at the meeting scheduling example in optaplanner-examples, to get inspired on how to model it. Also see the Time Grain pattern in the docs in the section Design Patterns. The school timetabling quickstart follows the Timeslot pattern instead.
Before I start my question, I will point out that this is for an assignment in a programming course I am doing, and I'm afraid the code needs to be in VB.
Scenario:
We are writing an app to help manage a veterinary clinic. There is a legacy MySQL database which cannot be changed. Information relating to pets and their owners are stored in two separate tables ("pets" table and "owners" table, and the tables are linked by the FK of CustomerId. We are able to use our choice of data access technologies and ORMs, and I have chosen to use EF to take advantage of the Change Tracking (I'd prefer to not have to write this code).
What I need to do is create an Entity Framework DbSet that contains information from both the pet and owner tables. I have looked at Table splitting in EF, but the two "entities" of pet and owner do not have the same primary key (which as I understand Table Splitting is required).
I have reviewed the following articles, and they have not helped:
Entity Framework and DbSets
DbSet in Entity Framework
Return data from two tables with Entity Framework
I am using EF6 and the "Code First from existing Database" workflow.
My Pet class looks like (I've removed the auto generated data annotations for brevity):
Partial Public Class Pet
Public Sub New()
bookings = New HashSet(Of Booking)()
stays = New HashSet(Of Stay)()
End Sub
Public Property petID As Integer
Public Property petName As String
Public Property species As String
Public Property breed As String
Public Property DOB As Date?
Public Property gender As String
Public Property weight As Single?
Public Property customerID As Integer?
Public Overridable Property bookings As ICollection(Of Booking)
Public Overridable Property customer As Customer
Public Overridable Property stays As ICollection(Of Stay)
End Class
My Customer class:
Partial Public Class Customer
Public Sub New()
pets = New HashSet(Of Pet)()
End Sub
Public Property customerID As Integer
Public Property title As String
Public Property firstName As String
Public Property lastName As String
Public Property gender As String
Public Property DOB As Date?
Public Property email As String
Public Property phone1 As String
Public Property phone2 As String
Public Property street1 As String
Public Property street2 As String
Public Property suburb As String
Public Property state As String
Public Property postcode As String
Public Overridable Property state1 As State
Public Overridable Property pets As ICollection(Of Pet)
Public ReadOnly Property FullName() As String
Get
Return $"{Me.lastName}, {Me.firstName}"
End Get
End Property
End Class
I also have a PetInfo class that does NOT map to the DB:
Public Class PetInfoModel
Public Property PetID As Integer
Public Property PetName As String
Public Property Species As String
Public Property Breed As String
Public Property DOB As Date
Public Property Gender As String
Public Property Weight As Decimal
Public Property OwnerFirstName As String
Public Property OwnerLastName As String
Public ReadOnly Property OwnerName() As String
Get
Return $"{OwnerLastName}, {OwnerFirstName}"
End Get
End Property
End Class
Now for the hard part: I would like to be able to use the PetInfoModel as a DbSet in my context to take advantage of the EF change tracking.
If it makes any difference (I don't think it should), I am using WPF MVVM and Caliburn.Micro for the UI. The ultimate goal is to get a List bound to a WPF datagrid.
Any assistance or suggestions would be more than welcome. Thanks for your time and efforts.
Regards
Steve Teece
I'm not very familiar with VB, so I'll have to write the answer in C#, I think you get the gist.
So you have DbSet<Pet> Pets and DbSet<Customer> Customers and you want to create something that acts as if it was a DbSet<PetInfoModel> PetInfoModels.
Are you sure you want something that acts like a DbSet? You want to be able to Add / Find / Attach / Remove PetInfoModels? Or do you only want to query data?
Problems with PetInfoModel
It seems to me that you get into troubles, if you want to Add a new PetInfoModel with a zero PetId, and the name of an existing Customer:
Add(new PetInfoModel
{
PetId = 0;
PetName = "Felix"
OwnerFirstName = "John",
OwnerLastName = "Doe",
Species = "Cat",
...
});
Add(new PetInfoModel
{
PetId = 0;
PetName = "Nero"
OwnerFirstName = "John", // NOTE: Same owner name
OwnerLastName = "Doe",
Species = "Dog",
...
});
Do we have one Customer with two Pets: a Cat and a Dog? Or do we have two Customers, with the same name, each with one Pet?
If you want more than just query PetInfoModels (Add / Update / Remove), you'll need to find a solution for this. I think most problems will be solved if you add a CustomerId. But then again: your PetInfoModel would just be a subset of the properties of a "Pet with his Owner", making it a bit useless to create the idea of a PetInfoModel
Anyway: let's assume you've defined a proper PetInfoModel and you really want to be able to Create / Retrieve / Update / Delete (CRUD) PetInfoModels as if you have a database table of PetInfoModels.
Database versus Repository
You should realize what your DbContext represents. It represents your database. The DbSet<...> properties of your DbContext represent the tables in your database. Your database does not have a table with PetInfoModels, hence your DbContext should not have this table.
On the other hand: Quite often you'll see a wrapper class around your DbContext that represents the things that can be stored in your Repository. This class is usually called a Repository.
In fact, a Repository only tells you that your data is stored, not how it is stored: it can be a CSV-file, or a database with a table structure different than the data sequences that can be handled by your repository.
IMHO I think it is wise to let your DbContext represent your database and create a Repository class that represents the stored data in a format that users of your database want.
As a minimum, I think a Repository should be able to Create / Retrieve / Update / Delete (CRUD) Customers and Pets. Later we'll add CRUD functions for PetInfoModels.
Customers and Pets
A RepositoryItem is something that can be stored / queried / removed from the repository. Every RepositoryItem can be identified by a primary key
interface IRepositoryItem<TRepositoryItem> : IQueryable<TRepositoryItem>
where TRepositoryItem : class
{
TRepositoryItem Add(TRepositoryItem item);
TRepositoryItem Find (params object[] keyValues);
void Remove(TRepositoryItem item);
}
To guarantee this primary key, I created an interface IID and let all my DbSet classes implement this interface. This enhances Find and Remove:
interface IID
{
int Id {get; }
}
class Student : IId
{
public int Id {get; set;}
...
}
interface IRepositoryItem<TRepositoryItem> : IQueryable<TRepositoryItem>
where TRepositoryItem : IID
{
TRepositoryItem Add(TRepositoryItem item);
TRepositoryItem Find (int id);
void Remove(TRepositoryItem item);
// or remove the item with primary key:
void Remove(int id);
}
If we have a DbSet the implementation of an IRespositoryItem is easy:
class RepositoryDbSet<TRepositoryItem> : IRepositoryItem<TRepositoryItem>
where TRepositoryItem : class
{
public IDbSet<TRepositoryItem> DbSet {get; set;}
public TRepositoryItem Add(TRepositoryItem item)
{
return this.DbSet.Add(item);
}
public TRepositoryItem Find (params object[] keyValues)
{
return this.DbSet.Find(keyValues);
}
public void Remove(TRepositoryItem item)
{
return this.DbSet.Remove(item);
}
public void Remove(TRepository
// implementation of IQueryable / IEnumerable is similar: use this.DbSet
}
If you defined interface IID:
public TRrepositoryItem Find(int id)
{
return this.DbSet.Find(id);
}
public void Remove(int id)
{
TRepositoryItem itemToRemove = this.Find(id);
this.DbSet.Remove(itemToRemove);
}
Now that we've defined the class that represents a set in the Repository, we can start creating the Repository itself.
class VetRepository : IDisposable
{
public VetRepository(...)
{
this.dbContext = new DbContext(...);
this.customers = new RepositoryItem<Customer> {DbSet = this.dbContext.Customers};
this.pets = new RepositoryItm<Pet> {DbSet = this.dbContext.Pets};
}
private readonly DbContext dbContext; // the old database
private readonly IRepositoryItem<Customer> customers;
private readonly IRepositoryItem<Pet> pets;
// TODO IDisposable: Dispose the dbcontext
// Customers and Pets:
public IRepositoryItem<Customer> Customers => this.customers;
public IRepositoryItem<Pet> Pets => this.pets;
IRepositoryItem<PetInfoModel> PetInfoModels = // TODO
public void SaveChanges()
{
this.DbContext.SaveChanges();
}
// TODO: SaveChangesAsync
}
We still have to create a repository class that represents the PetInfoModels. This class should implement IRepositoryItem. This way users of the repository won't notice that the database doesn't have a table with PetInfoModels
class RepositoryPetInfoModel : IRepositoryItem<PetInfoModel>
{
// this class needs both Customers and Pets:
public IDbSet<Customer> Customers {get; set;}
public IDbSet<Pet> Pets {get; set;}
public PetInfoModel Add(PetInfoModel petInfo)
{
// check the input, reject if problems
// decide whether we have a new Pet for new customer
// or a new pet for existing customer
// what to do with missing fields?
// what to do if Customer exists, but his name is incorrect?
Pet petToAdd = ... // extract the fields from the petInfo
Customer customerToAdd = ... // or: customerToUpdate?
// Add the Pet,
// Add or Update the Customer
}
Hm, do you see how much troubles your PetInfoModel encounters if you really want to CRUD?
Retrieve is easy: just create a Query that joins the Pet and his Owner and select the fields for a PetInfoModel. For example
IQueryable<PetInfoModel> CreateQuery()
{
// Get all Customers with their Pets
return this.Customers.Join(this.Pets
{
customer => customer.Id, // from every Customer take the primary key
pet => pet.CustomerId, // from every Pet take the foreign key
// Result selector: take every Customer with a matching Pet
// to make a new PetInfoModel
(customer, pet) => new PetInfoModel
{
CustomerId = customer.Id,
OwnerFirstName = customer.FirstName,
...
PetId = pet.Id,
PetName = pet.Name,
...
});
}
Update is also fairly easy: PetId and CustomerId should exist. Fetch the Pet and Customer and update the fields with the corresponding fields from PetInfoModel
Delete will lead to problems again: what if the Owner has a 2nd Pet? Delete only the Pet but not the Owner? Or Delete the Owner and all hist Pets, inclusive the Pets you didn't mention?
Conclusion
If you only want to query data, then it won't be a problem to introduce a PetInfoModel.
To really CRUD PetInfoModels, you'll encounter several problems, especially with the concept of Owners with two Pets, and Owners having the same name. I would advise not to CRUD for PetInfoModels, only query them.
A proper separation between your database and the concept of "stored data" (Repository) is advisable, because it allows you to have a database that differs from the model that users of your Repository see.
I am looking for solve my issue.
I have something like this.
class A{
#OneToMany(mappedBy="a")
private List<B> bs;
}
class B {
#ManyToOne
private A a;
private String name;
#Lob
private byte[] img;
}
I want get all records from database except img field from relationship B.
Maybe create query in JPA but how do this correct?
How can I do that?
You could define img as lazy:
#Basic(fetch=FetchType.LAZY)
#Lob
private byte[] img;
That way the image is only loaded when you access the getter.
Second option is to create a DTO (Data transfer object) using the constructor expression:
package dto;
public class BDto {
private final String name;
private final A a;
public BDto(String name, A a) {
this.name = name;
this.a = a;
}
// getters for both fields
}
Then you can create a query like:
select new dto.BDto(b.name, b.a) from B b where b.a.received = true
Important notice: the dto must be used in the query fully qualified (e.g. dto.BDto)
I have the following Entity
#Entity
public class Project implements Serializable {
#Id
private Integer project_id;
private String project_name;
other attributes
#OneToOne
#JoinColumn(name = "lead_emp_no", referencedColumnName = "emp_no")
private Employee projectLead;
// but the following two relationships need to be a connect by:
#OneToOne
#JoinColumn(name = "lead_boss_emp_no", referencedColumnName = "emp_no")
private Employee projectLeadBoss;
#OneToOne
#JoinColumn(name = "lead_bosses_boss_emp_no", referencedColumnName = "emp_no")
private Employee projectLeadBossesBoss;
With this setup, we have to manually maintain the employee numbers for the Lead's boss and the Lead's Boss's boss. This relationship is [somewhat] already available knowing the projectLead employee:
The Employee Entity is as follows:
#Entity
public class Employee implements Serializable {
#Id
private Integer emp_no;
private Integer bosses_emp_no;
Is it possible to get my Project entity to connect to the boss and bosses Employee based on projectLead? In single query I'd like to get a table of all projects and their lead's hierarchy. I'm open to entity redesign.
You can replace the bosses_emp_no in Employee should with a more helpful boss:
#Entity
public class Employee implements Serializable {
#Id
private Integer emp_no;
#OneToOne
#JoinColumn(name = "boss_emp_no", referencedColumnName = "emp_no")
private Employee boss;
Then you simply add a couple of delegating methods to Project
public Employee getProjectLeadBoss() {
return this.projectLead.getBoss();
}
public Employee getProjectLeadBossesBoss() {
return this.getProjectLeadBoss().getBoss();
}
How would you annotate this bean to be mapped in hibernate ?
#Entity
public class PerformanceValues implements Serializable{
private static final long serialVersionUID = 1234850675335166109L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//key is mass, value is distance needed
private Map<Integer, Integer> massToDist;
}
Each performanceValues entity has a unique map, and each map can be related to only one PerformanceValues (I guess this is a oneToOne relationship)
Thanks