I am trying to design a specific part of a domain model for a website.
The issue I have is representing a sort of availability matrix with days of the week (Day) and time slots (TimeSlot).
Possible values for Day are:
Monday
Tuesday
Wednesday
...
Possible values for TimeSlot are:
Before school
Morning
Noon
Afternoon
After school
Evening
Nighttime
Currently, I have a JPA entity with the two above enums (Day and TimeSlot) as fields:
#Entity
public class DayToTimeSlot {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#NotNull(groups = { Default.class, Validation.AdvertisementCreation.class })
#Enumerated
private Day day;
#NotNull(groups = { Default.class, Validation.AdvertisementCreation.class })
#Enumerated
private TimeSlot timeSlot;
}
I then populate a DayToTimeSlot reference table with all possible values/combinations.
And finally, I have an Advertisement entity that has a #ManyToMany java.util.Set of DayToTimeSlot instances as follows:
#ManyToMany
private Set<DayToTimeSlot> dayToTimeSlots;
This results in a advertisement_day_to_time_slots join table which can grow very quickly.
Is there not a better way to model this availability matrix?
I suppose you have only 7Days * 7 TimeSlots = 49 entries in the DayToTimeSlot table and then you only reference them in the Advertisement table.
To remain fully flexible I don't see any better ways to model the availability matrix. But your problem seems to be the association table. I would put myself the following questions: how quickly does it grow? Will you reach 1million in the next 5 years? To search in such 1 million-entries table is not such a big problem.
Independently of your answer to the last question you could think of caching the DayToTimeSlot table.
Besides, if you really do not want to search Advertisment by DayToTimeSlot, you could save the associations as a String field in Advertisment and load them when the getter getDayToTimeSlots() is called. E.g. On save of the Advertisment you itrate through the whole set of DayToTimeSlot, ad build a string of form : "MONDAY_BEFORE_SCHOOL, TUESDAY_NOON,..." and save it. On loading of an Advertisment you process the string in order to create the Set. Of course, in this case you will delete the #ManyToMany reference.
But another feasible solution would be the following (if for every week day you have a single entry of TimeSlot):
If you want to search for Advertisment only by day, or if you don't mind writing a bit more complex queries, then you could to add 7 new fields: mondayAvailability, tuesdayAvailability, to the Advertisment entity, save the data there and get rid of the #ManyToMany relationship..
Related
I'm using JPA, Hibernate and Postgres. I'd like the code to be as solution neutral as possible, where JPA is a given.
My simplified entity looks like this:
#Entity
#Table(name = "example")
public class ExampleEntity {
#Id
#GeneratedValue
private UUID id;
private String businessId; //format is YYYY000001 where YYYY = current year. Current assumption: number is incrementing and reset every year, number is always filled up with leading 0 to make the key 10 digits
}
I always have a generated UUID as the primary key. The business-id shall only be set if a certain state has been reached and is therefore unrelated to when the entity has been created. I would like the database to take care of the incrementing number.
Preferably I'd like to solve this through JPA, but also see a "dirtier" solution where I fetch the sequence-id and generate the business-key in my logic.
I am working with Eclipselink and having issue with using secondary table.
I have two tables as below.
Student with columns student_id(Primary Key), student_name etc.
Registration with columns student_id(FK relationship with Student table), course_name (with not null constraint) etc.
The requirement is student may or may not have registration. If student has registration, the data should be persisted to Registration table as well. Otherwise only Student table should be persisted.
My code snippet is as below.
Student.java
------------
#Entity
#Table(name = "STUDENT")
#SecondaryTable(name = "REGISTRATION")
#Id
#Column(name = "STUDENT_ID")
private long studentId;
#Basic(optional=true)
#Column(name = "COURSE_NAME", table = "REGISTRATION")
private String courseName;
I tried the following scenarios.
1. Student with registration - Working fine. Data is added to both Student and Registration tables
2. Student without registration - Getting error such as 'COURSE_NAME' cannot be null.
Is there a way to prevent persisting into secondary table?
Any help is much appreciated.
Thanks!!!
As #Eelke states, the best solution is to define two classes and a OneToOne relationship.
Potentially you could also use inheritance, having a Student and a RegisteredStudent that adds the additional table. But the relationship is a much better design.
It‘s possible using a DescriptorEventListener. The aboutToInsert and aboutToUpdate callbacks have access to the DatabaseCalls and may even remove the statements hitting the secondary table.
Register the DescriptorEventListener with the ClassDescriptor of the entity. For registration use a DescriptorCustomizer specified in a Customizer annotation at the entity.
However, you will not succeed fetching the entities back again later on. EclipseLink uses inner joins when selecting from the secondary table, so that the row of the primary table will be gone in the results.
I have a form which has several unique fields for that form say, School, class,
Location etc and several fields that needs to be repeated say, , Student id, Student name, Student roll no.
I want to repeat the last three fields 10 times.
Secondly, how do I store it in database (MySQL using doctrine). I mean rows should be created depending on the number of last three fields and the first three fields should remain same.
How do I achieve this in a Symfony2?
Any help will be highly appreciated.
The form structure is something like this:
School Class Location
Sno Student id Student Name Student Roll No
1.2.3.4.5.And so on......Edit: Fixed at 10 rows
Update
Entity Class:
class StudentForm
{
private $id;
private $school;
private $class;
private $location;
private $id;
private $name;
private $rollNo;
getter/setter methods follow
Well. Up to now I always used the JavaScript provided in the example. You could try to call this function 10 times, or see how the markup is created. But then again this will probably not work with the code in the controller from the example.
You can always create the whole markup yourself and then just see who that data is received in your controller. But then again, there wasn't too much information in your initial description. It seemed you currently don't have the code for your entities.
After a good long process of trial and error, I finally managed to do it.
The doctrine documentation helped creating a collection (though it didn't give enough information on saving the objects) for the repeating rows and so did the inputs of #Dirk Olbertz here. Thank you.
I have the following Entity:
#Entity
public class Question {
#Id
#GeneratedValue
private Integer id;
private String content;
private boolean visible;
private Date displayDate;
}
Only one Question can be visible in the database all the time. The requirement for the list in the dataTable is that the first line must be the visible Question and the others must be ordered by displayDate. Any idea?
Sorting is usually performed in DB (model) side, not in JSF (view) side. Your SQL (and equivalently also JPQL) query should be written that way that it returns exactly the rows you need, without any necessary postprocessing (the DB is namely endless much more efficient in the selecting job than Java/JSF).
In plain SQL terms, that would be
(...) ORDER BY visible DESC, displayDate ASC
If you're using JPA, you should surely be able to extract the proper JPQL syntax for that
(...) FROM Question q (...) ORDER BY q.visible DESC, q.displayDate ASC
I'm continuing exploring Cassandra and I would like to create Student <=> Course relation which is similar to Many-to-Many on RDBMS.
In term of Queries I will use the following query;
Retrieve all courses in which student enrolled.
Retrieve all students enrolled in specific course.
Let's say that I create to Column Families. one for Course and another for Student.
CREATE COLUMN FAMILY student with comparator = UTF8Type AND key_validation_class=UTF8Type and column_metadata=[
{column_name:firstname,validation_class:UTF8Type}
{column_name:lastname,validation_class:UTF8Type}
{column_name:gender,validation_class:UTF8Type}];
CREATE COLUMN FAMILY course with comparator = UTF8Type AND key_validation_class=UTF8Type and column_metadata=[
{column_name:name,validation_class:UTF8Type}
{column_name:description,validation_class:UTF8Type}
{column_name:lecturer,validation_class:UTF8Type}
{column_name:assistant,validation_class:UTF8Type}];
Now how should I move on?
Should I create third Column Family with courseID:studentId CompisiteKey? if yes, Can I use Hector to query by only one (left or right) Composite key component?
Please help.
Update:
Following the suggestion I created the following Schema:
For Student:
CREATE COLUMN FAMILY student with comparator = UTF8Type and key_validation_class=UTF8Type and default_validation_class=UTF8Type;
and then we will add some data:
set student['student.1']['firstName']='Danny'
set student['student.1']['lastName']='Lesnik'
set student['student.1']['course.1']=''
set student['student.1']['course.2']='';
Create column Family for Course:
CREATE COLUMN FAMILY course with comparator = UTF8Type and key_validation_class=UTF8Type and default_validation_class=UTF8Type;
add some data:
set course['course.1']['name'] ='History'
set course['course.1']['description'] ='History Course'
set course['course.1']['name'] ='Algebra'
set course['course.1']['description'] ='Algebra Course'
and Finally Student In Course:
CREATE COLUMN FAMILY StudentInCourse with comparator = UTF8Type and key_validation_class=UTF8Type and default_validation_class=UTF8Type;
add data:
set StudentInCourse['studentIncourse.1']['student.1'] ='';
set StudentInCourse['studentIncourse.2']['student.1'] ='';
I defined a data model below but it is easier to decribe the object model first and then dive into the row model, so from PlayOrm's perspective you would have
public class Student {
#NoSqlId
private String id;
private String firstName;
private String lastName;
#ManyToMany
private List<Course> courses = new ArrayList(); //constructing avoids nullpointers
}
public class Course {
#NoSqlId
private String id;
private String name;
private String description
#ManyToOne
private Lecturer lecturer;
#ManyToMany
private CursorToMany students = new CursorToManyImpl();
}
I could have used List in course but I was concerned I may get OutOfMemory if too many students take a course over years and years and years. NOW, let's jump to what PlayOrm does and you can do something similar if you like
A single student row would look like so
rowKey(the id in above entity) = firstName='dean',
lastName='hiller' courses.rowkey56=null, courses.78=null, courses.98=null, courses.101=null
This is the wide row where we have many columns with the name 'fieldname' and 'rowkey to actual course'
The Course row is a bit more interesting....because the user thinks loading al the Students for a single course could cause out of memory, he uses a cursor which only loads 500 at a time as you loop over it.
There are two rows backing the Course in this case that PlayOrm will have. Sooo, let's take our user row above and he was in course rowkey56 so let's describe that course
rowkey56 = name='coursename', description='somedesc', lecturer='rowkey89ToLecturer'
Then, there is another row in the some index table for the students(it is a very wide row so supports up to millions of students)
indexrowForrowkey56InCourse = student34.56, student39.56, student.23.56....
into the millions of students
If you want a course to have more than millions of students though, then you need to think about partitioning whether you use playOrm or not. PlayOrm does partitioning for you if you need though.
NOTE: If you don't know hibernate or JPA, when you load the above Student, it loads a proxy list so if you start looping over the courses, it then goes back to the noSQL store and loads the Courses so you don't have to ;).
In the case of Course, it loads a proxy Lecturer that is not filled in until you access a property field like lecturer.getName(). If you call lecturer.getId(), it doesn't need to load the lecturer since it already has that from the Course row.
EDIT(more detail): PlayOrm has 3 index tables Decimal(stores double, float, etc and BigDecimal), Integer(long, short, etc and BigInteger and boolean), and String index tables. When you use CursorToMany, it uses one of those tables depending on the FK type of key. It also uses those tables for it's Scalable-SQL language. The reason it uses a separate row on CursorToMany is just so clients don't get OutOfMemory on reading a row in as the toMany could have one million FK's in it in some cases. CursorToMany then reads in batches from that index row.
later,
Dean