diff --git a/pom.xml b/pom.xml index 7d11d05..18af176 100644 --- a/pom.xml +++ b/pom.xml @@ -54,11 +54,22 @@ github-api 1.93 - + - org.xerial - sqlite-jdbc - 3.23.1 + mysql + mysql-connector-java + 8.0.12 + + + com.h2database + h2 + 1.4.197 + + + + org.hibernate + hibernate-core + 5.3.6.Final diff --git a/src/main/java/nl/andrewlalis/Main.java b/src/main/java/nl/andrewlalis/Main.java index f0be3c2..a7f8af3 100644 --- a/src/main/java/nl/andrewlalis/Main.java +++ b/src/main/java/nl/andrewlalis/Main.java @@ -1,10 +1,14 @@ package nl.andrewlalis; +import nl.andrewlalis.model.Student; +import nl.andrewlalis.model.database.DbUtil; import nl.andrewlalis.ui.control.command.CommandExecutor; import nl.andrewlalis.ui.control.command.executables.*; import nl.andrewlalis.ui.view.InitializerApp; import nl.andrewlalis.util.CommandLine; import nl.andrewlalis.util.Logging; +import org.hibernate.Session; +import org.hibernate.SessionFactory; import java.util.Map; import java.util.logging.Logger; @@ -46,6 +50,13 @@ public class Main { logger.info("GithubManager for Github Repositories in Educational Organizations.\n" + "© Andrew Lalis (2018), All rights reserved.\n" + "Program initialized."); + + SessionFactory factory = DbUtil.getSessionFactory(); + Session session = factory.openSession(); + session.beginTransaction(); + System.out.println(session.save(new Student(1, "a", "a@e.com", "git", null))); + session.getTransaction().commit(); + session.close(); } } diff --git a/src/main/java/nl/andrewlalis/git_api/GithubManager.java b/src/main/java/nl/andrewlalis/git_api/GithubManager.java index 4f8b148..9748239 100644 --- a/src/main/java/nl/andrewlalis/git_api/GithubManager.java +++ b/src/main/java/nl/andrewlalis/git_api/GithubManager.java @@ -177,7 +177,7 @@ public class GithubManager { GHRepository repo = this.createRepository(team.generateUniqueName(prefix), taTeam.getGithubTeam(), team.generateRepoDescription(), false, true, true); if (repo == null) { - logger.severe("Repository for student team " + team.getId() + " could not be created."); + logger.severe("Repository for student team " + team.getNumber() + " could not be created."); return; } @@ -254,7 +254,7 @@ public class GithubManager { */ private void inviteStudentsToRepos(StudentTeam team, GHRepository assignmentsRepo) { try { - logger.finest("Adding students from team: " + team.getId() + " as collaborators."); + logger.finest("Adding students from team: " + team.getNumber() + " as collaborators."); for (Student student : team.getStudents()) { GHUser user = this.github.getUser(student.getGithubUsername()); diff --git a/src/main/java/nl/andrewlalis/model/Person.java b/src/main/java/nl/andrewlalis/model/Person.java index 50b420a..62639b9 100644 --- a/src/main/java/nl/andrewlalis/model/Person.java +++ b/src/main/java/nl/andrewlalis/model/Person.java @@ -1,31 +1,53 @@ package nl.andrewlalis.model; +import nl.andrewlalis.model.database.BaseEntity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + /** * A generic object that students, teaching assistants, and professors can extend from. This covers all the basic * functionality that applies to anyone in the system. */ -public abstract class Person { +@Entity(name = "Person") +@Table(name = "persons") +public abstract class Person extends BaseEntity { /** * The unique identification number for this person. (P- or S-Number) */ + @Column(name="number") protected int number; /** * The person's first and last name. */ + @Column(name="name") protected String name; /** * The person's email address. */ + @Column(name="email_address") protected String emailAddress; /** * The person's github username. */ + @Column(name="github_username") protected String githubUsername; + /** + * Constructs an empty default Person. + */ + public Person() { + this.number = -1; + this.name = null; + this.emailAddress = null; + this.githubUsername = null; + } + /** * Constructs a Person from only a github username, which is, in some cases, enough to perform a lot of actions. * @param githubUsername The person's github username. @@ -58,18 +80,34 @@ public abstract class Person { return this.number; } + public void setNumber(int number) { + this.number = number; + } + public String getName(){ return this.name; } + public void setName(String name) { + this.name = name; + } + public String getEmailAddress(){ return this.emailAddress; } + public void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } + public String getGithubUsername(){ return this.githubUsername; } + public void setGithubUsername(String githubUsername) { + this.githubUsername = githubUsername; + } + /** * Determines if two persons are the same. This is defined as: * Two persons are equal if at least one of their personal data points is identical. Because each of the data points diff --git a/src/main/java/nl/andrewlalis/model/Student.java b/src/main/java/nl/andrewlalis/model/Student.java index e347b58..f68df82 100644 --- a/src/main/java/nl/andrewlalis/model/Student.java +++ b/src/main/java/nl/andrewlalis/model/Student.java @@ -1,12 +1,15 @@ package nl.andrewlalis.model; +import javax.persistence.*; +import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.logging.Logger; /** * Represents one student's github information. */ +@Entity(name = "Student") +@Table(name="students") public class Student extends Person { private static final Logger logger = Logger.getLogger(Student.class.getName()); @@ -17,7 +20,20 @@ public class Student extends Person { /** * A list of partners that the student has said that they would like to be partners with. */ - private List preferredPartners; + @ManyToMany + @JoinTable( + name = "student_preferred_partners", + joinColumns = { @JoinColumn(name = "student_id")}, + inverseJoinColumns = {@JoinColumn(name = "preferred_partner_id")} + ) + private List preferredPartners; + + /** + * Constructs an empty student object. + */ + public Student() { + this.preferredPartners = new ArrayList<>(); + } /** * Constructs a student similarly to a Person, but with an extra preferredPartners list. @@ -28,24 +44,31 @@ public class Student extends Person { * @param preferredPartners A list of this student's preferred partners, as a list of integers representing the * other students' numbers. */ - public Student(int number, String name, String emailAddress, String githubUsername, List preferredPartners) { + public Student(int number, String name, String emailAddress, String githubUsername, List preferredPartners) { super(number, name, emailAddress, githubUsername); this.preferredPartners = preferredPartners; } - public List getPreferredPartners() { + public List getPreferredPartners() { return this.preferredPartners; } + public void setPreferredPartners(List preferredPartners) { + this.preferredPartners = preferredPartners; + } + + public void addPreferredPartner(Student student) { + this.preferredPartners.add(student); + } + /** - * Using a given map of all students, returns a student's preferred team. - * @param studentMap A mapping from student number to student for all students who have signed up. - * @return A team with unknown id, comprised of this student's preferred partners. + * Returns a student's preferred team, including himself. + * @return A team with unknown number, comprised of this student's preferred partners. */ - public StudentTeam getPreferredTeam(Map studentMap) { + public StudentTeam getPreferredTeam() { StudentTeam t = new StudentTeam(); - for (int partnerNumber : this.getPreferredPartners()) { - t.addMember(studentMap.get(partnerNumber)); + for (Student partner : this.getPreferredPartners()) { + t.addMember(partner); } t.addMember(this); return t; diff --git a/src/main/java/nl/andrewlalis/model/StudentTeam.java b/src/main/java/nl/andrewlalis/model/StudentTeam.java index 7da57ff..6d987e1 100644 --- a/src/main/java/nl/andrewlalis/model/StudentTeam.java +++ b/src/main/java/nl/andrewlalis/model/StudentTeam.java @@ -2,21 +2,29 @@ package nl.andrewlalis.model; import org.kohsuke.github.GHRepository; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Transient; import java.util.Arrays; /** * Represents one or more students' collective information. */ -public class StudentTeam extends Team{ +@Entity(name = "StudentTeam") +@Table(name = "student_teams") +public class StudentTeam extends Team { /** * The repository belonging to this team. */ + @Transient private GHRepository repository; /** * The TATeam responsible for this student team. */ + @ManyToOne private TATeam taTeam; public StudentTeam() { @@ -62,11 +70,11 @@ public class StudentTeam extends Team{ * Generates a unique name which is intended to be used for the repository name of this team. * @param prefix A prefix to further reduce the chances of duplicate names. * It is suggested to use something like "2018_OOP" - * @return A string comprised of the prefix, team id, and student number of each team member. + * @return A string comprised of the prefix, team number, and student number of each team member. */ public String generateUniqueName(String prefix) { StringBuilder sb = new StringBuilder(prefix); - sb.append("_team_").append(this.id); + sb.append("_team_").append(this.number); for (Student s : this.getStudents()) { sb.append('_').append(s.getNumber()); } @@ -79,7 +87,7 @@ public class StudentTeam extends Team{ */ public String generateRepoDescription() { StringBuilder sb = new StringBuilder(); - sb.append("Group ").append(this.id).append(": "); + sb.append("Group ").append(this.number).append(": "); for (int i = 0; i < this.memberCount(); i++) { sb.append(this.getStudents()[i].getName()); if (i != this.memberCount()-1) { diff --git a/src/main/java/nl/andrewlalis/model/TATeam.java b/src/main/java/nl/andrewlalis/model/TATeam.java index 99dc211..cc26560 100644 --- a/src/main/java/nl/andrewlalis/model/TATeam.java +++ b/src/main/java/nl/andrewlalis/model/TATeam.java @@ -2,14 +2,19 @@ package nl.andrewlalis.model; import org.kohsuke.github.GHTeam; +import javax.persistence.Entity; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Transient; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** - * Represents a teaching assistant team, which is itself a 'team' in the organization. This class is used for parsing - * json from requests to github to get a list of all teams in the organization. + * Represents a teaching assistant team, which is itself a 'team' in the organization. */ +@Entity(name = "TATeam") +@Table(name = "ta_teams") public class TATeam extends Team { /** @@ -20,11 +25,13 @@ public class TATeam extends Team { /** * The Github team associated with this team. */ + @Transient private GHTeam githubTeam; /** * A list of all student teams for which this TA team is responsible. */ + @OneToMany private List studentTeams; /** diff --git a/src/main/java/nl/andrewlalis/model/TeachingAssistant.java b/src/main/java/nl/andrewlalis/model/TeachingAssistant.java index 7362c48..1c2787b 100644 --- a/src/main/java/nl/andrewlalis/model/TeachingAssistant.java +++ b/src/main/java/nl/andrewlalis/model/TeachingAssistant.java @@ -1,8 +1,13 @@ package nl.andrewlalis.model; +import javax.persistence.Entity; +import javax.persistence.Table; + /** * Represents an administrator in the organization, who manages student teams. */ +@Entity(name = "TeachingAssistant") +@Table(name = "teaching_assistants") public class TeachingAssistant extends Person { /** diff --git a/src/main/java/nl/andrewlalis/model/Team.java b/src/main/java/nl/andrewlalis/model/Team.java index f1b23db..eee12cb 100644 --- a/src/main/java/nl/andrewlalis/model/Team.java +++ b/src/main/java/nl/andrewlalis/model/Team.java @@ -1,5 +1,8 @@ package nl.andrewlalis.model; +import nl.andrewlalis.model.database.BaseEntity; + +import javax.persistence.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -8,39 +11,48 @@ import java.util.List; * An abstract Team object from which both Teaching Assistant and Student teams can be built. A Team consists of a list * of members, and a unique identification number. */ -public abstract class Team { +@Entity(name = "Team") +@Table(name = "teams") +public abstract class Team extends BaseEntity { /** * An identification number unique to this team alone. */ - protected int id; + @Column(name = "number") + protected int number; /** * A list of members of this team. */ + @OneToMany + @JoinTable( + name = "team_members", + joinColumns = {@JoinColumn(name = "team_id")}, + inverseJoinColumns = {@JoinColumn(name = "person_id")} + ) private List members; /** - * Constructs this team with the given id. - * @param id The id to assign to this team. + * Constructs this team with the given number. + * @param number The number to assign to this team. */ - public Team(int id) { - this.id = id; + public Team(int number) { + this.number = number; this.members = new ArrayList<>(); } /** - * @param newId The new id number to assign to this team. + * @param newId The new number number to assign to this team. */ - public void setId(int newId) { - this.id = newId; + public void setNumber(int newId) { + this.number = newId; } /** - * @return This team's id number. + * @return This team's number number. */ - public int getId() { - return this.id; + public int getNumber() { + return this.number; } /** @@ -123,7 +135,7 @@ public abstract class Team { /** * Checks if an object is equal to this team. First checks if the other object is a Team, and then if it has the - * same id and team size. If both of those conditions are met, then it will check that all team members are the + * same number and team size. If both of those conditions are met, then it will check that all team members are the * same. * @param obj The object to check for equality. * @return True if the two objects represent the same team, or false otherwise. @@ -132,7 +144,7 @@ public abstract class Team { public boolean equals(Object obj) { if (obj instanceof Team) { Team team = (Team) obj; - return team.getId() == this.getId() && this.hasSameMembers(team); + return team.getNumber() == this.getNumber() && this.hasSameMembers(team); } return false; } @@ -143,7 +155,7 @@ public abstract class Team { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("Team of ").append(this.memberCount()).append(" members:\tID: ").append(this.id).append('\n'); + sb.append("Team of ").append(this.memberCount()).append(" members:\tID: ").append(this.number).append('\n'); for (Person person : this.members) { sb.append(person.toString()).append('\n'); } diff --git a/src/main/java/nl/andrewlalis/model/database/BaseEntity.java b/src/main/java/nl/andrewlalis/model/database/BaseEntity.java new file mode 100644 index 0000000..a31e294 --- /dev/null +++ b/src/main/java/nl/andrewlalis/model/database/BaseEntity.java @@ -0,0 +1,28 @@ +package nl.andrewlalis.model.database; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +/** + * Defines a base entity which all others in the database extend from. + */ +@MappedSuperclass +public abstract class BaseEntity { + + /** + * The number for this entity. + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/src/main/java/nl/andrewlalis/model/database/Database.java b/src/main/java/nl/andrewlalis/model/database/Database.java deleted file mode 100644 index a62149d..0000000 --- a/src/main/java/nl/andrewlalis/model/database/Database.java +++ /dev/null @@ -1,266 +0,0 @@ -package nl.andrewlalis.model.database; - -import nl.andrewlalis.model.*; - -import java.sql.*; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -/** - * This class abstracts many of the functions needed for interaction with the application's SQLite database. - */ -public class Database { - - private static final int PERSON_TYPE_STUDENT = 0; - private static final int PERSON_TYPE_TA = 1; - - private static final int TEAM_TYPE_STUDENT = 0; - private static final int TEAM_TYPE_TA = 1; - private static final int TEAM_TYPE_TA_ALL = 2; - - private static final int TEAM_NONE = 1000000; - private static final int TEAM_TA_ALL = 1000001; - - private static final int ERROR_TYPE_TEAM = 0; - private static final int ERROR_TYPE_PERSON = 1; - private static final int ERROR_TYPE_SYSTEM = 2; - - /** - * The connection needed for all queries. - */ - private Connection connection; - - /** - * The logger for outputting debug info. - */ - private static final Logger logger = Logger.getLogger(Database.class.getName()); - static { - logger.setParent(Logger.getGlobal()); - } - - public Database(String databaseFilename) { - try { - this.connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFilename); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - /** - * Initializes the database from the table_init.sql script, which defines the table schema. - * Then, inserts some constant starter data from /sql/insert/types.sql. - * @return True if successful, false if not. - */ - public boolean initialize() { - List tableStatements = Utils.prepareStatementsFromFile("/sql/table_init.sql", this.connection); - for (PreparedStatement statement : tableStatements) { - try { - statement.execute(); - } catch (SQLException e) { - logger.severe("SQLException while executing prepared statement:\n" + statement.toString() + "\nCode: " + e.getErrorCode()); - return false; - } - } - logger.fine("Database tables initialized."); - List insertStatements = Utils.prepareStatementsFromFile("/sql/insert/types.sql", this.connection); - for (PreparedStatement statement : insertStatements) { - try { - statement.execute(); - } catch (SQLException e) { - logger.severe("SQLException while inserting into table:\n" + statement.toString() + "\nCode: " + e.getErrorCode()); - return false; - } - } - logger.fine("Initial types inserted."); - return true; - } - - /** - * Stores a person in the database. - * @param person The person object to store. - * @param personType The type of person to store, using a constant defined above. - * @return True if successful, false otherwise. - */ - public boolean insertPerson(Person person, int personType) { - try { - logger.finest("Storing person: " + person); - String sql = "INSERT INTO persons (id, name, email_address, github_username, person_type_id) VALUES (?, ?, ?, ?, ?);"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, person.getNumber()); - stmt.setString(2, person.getName()); - stmt.setString(3, person.getEmailAddress()); - stmt.setString(4, person.getGithubUsername()); - stmt.setInt(5, personType); - return stmt.execute(); - } catch (SQLException e) { - logger.severe("SQLException while inserting Person: " + person + '\n' + e.getMessage()); - return false; - } - } - - /** - * Stores a student in the database. - * @param student The student to store. - * @return True if the operation was successful, false otherwise. - */ - public boolean insertStudent(Student student) { - logger.finest("Storing student: " + student); - if (!insertPerson(student, PERSON_TYPE_STUDENT)) { - return false; - } - try { - String sql = "INSERT INTO students (person_id, chose_partner) VALUES (?, ?);"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, student.getNumber()); - stmt.setInt(2, student.getPreferredPartners().size() > 0 ? 1 : 0); - if (!stmt.execute()) { - return false; - } - // Storing partners. - String sqlPartner = "INSERT INTO student_preferred_partners (student_id, partner_id) VALUES (?, ?);"; - PreparedStatement stmtPartner = this.connection.prepareStatement(sqlPartner); - for (int partnerId : student.getPreferredPartners()) { - stmtPartner.setInt(1, student.getNumber()); - stmtPartner.setInt(2, partnerId); - stmtPartner.execute(); - } - return true; - } catch (SQLException e) { - logger.severe("SQL Exception while inserting Student into database.\n" + e.getMessage()); - e.printStackTrace(); - return false; - } - } - - /** - * Stores a teaching assistant in the database. - * @param ta The teaching assistant to store. - * @return True if successful, false otherwise. - */ - public boolean insertTeachingAssistant(TeachingAssistant ta) { - if (!insertPerson(ta, PERSON_TYPE_TA)) { - return false; - } - try { - String sql = "INSERT INTO teaching_assistants (person_id) VALUES (?);"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, ta.getNumber()); - stmt.execute(); - return true; - } catch (SQLException e) { - logger.severe("SQL Exception while inserting TeachingAssistant.\n" + e.getMessage()); - e.printStackTrace(); - return false; - } - } - - /** - * Stores a team in the database, and any persons who do not yet exist. - * @param team The team to store. - * @param type The type of team that this is. - * @param personType The type of people that this team is made of. - * @return True if successful, false otherwise. - */ - public boolean insertTeam(Team team, int type, int personType) { - try { - String sql = "INSERT INTO teams (id, team_type_id) VALUES (?, ?);"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, team.getId()); - stmt.setInt(2, type); - if (stmt.execute()) { - for (Person p : team.getMembers()) { - this.insertPerson(p, personType); - String sqlMembers = "INSERT INTO team_members (team_id, person_id) VALUES (?, ?);"; - PreparedStatement stmtMembers = this.connection.prepareStatement(sqlMembers); - stmtMembers.setInt(1, team.getId()); - stmtMembers.setInt(2, p.getNumber()); - stmtMembers.execute(); - } - } - return true; - } catch (SQLException e) { - logger.severe("SQLException while inserting team: " + team + '\n' + e.getMessage()); - return false; - } - } - - /** - * Stores a student team in the database. - * @param team The team to store. - * @return True if successful, false otherwise. - */ - public boolean insertStudentTeam(StudentTeam team) { - if (!this.insertTeam(team, TEAM_TYPE_STUDENT, PERSON_TYPE_STUDENT)) { - return false; - } - try { - String sql = "INSERT INTO student_teams (team_id, repository_name, teaching_assistant_team_id) VALUES (?, ?, ?);"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, team.getId()); - stmt.setString(2, (team.getRepository() == null) ? null : team.getRepository().getName()); - stmt.setInt(3, (team.getTaTeam() == null) ? TEAM_NONE : team.getTaTeam().getId()); - return stmt.execute(); - } catch (SQLException e) { - logger.severe("SQLException while inserting student team: " + team + '\n' + e.getMessage()); - e.printStackTrace(); - return false; - } - } - - /** - * Stores a list of student teams in the database. - * @param teams The list of teams to store. - * @return True if successful, or false if an error occurred. - */ - public boolean storeStudentTeams(List teams) { - for (StudentTeam team : teams) { - this.insertStudentTeam(team); - } - return true; - } - - /** - * Retrieves a list of preferred partners that each student has set. - * @param studentId The student id to search by. - * @return A list of student id's for all students that the given student wishes to be their partner. - */ - private List retrievePreferredPartners(int studentId) { - try { - logger.finest("Retrieving preferred partners of student: " + studentId); - String sql = "SELECT partner_id FROM student_preferred_partners WHERE student_id=?;"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, studentId); - ResultSet results = stmt.executeQuery(); - List partners = new ArrayList<>(); - while (results.next()) { - partners.add(results.getInt(1)); - } - return partners; - } catch (SQLException e) { - logger.severe("SQL Exception while retrieving preferred partners of student: " + studentId + '\n' + e.getMessage()); - e.printStackTrace(); - return new ArrayList<>(); - } - } - - /** - * Retrieves a student by their id. - * @param id The id of the student (student number) - * @return The student corresponding to this number, or null if it could not be found. - */ - public Student retrieveStudent(int id) { - try { - String sql = "SELECT * FROM persons WHERE id=?"; - PreparedStatement stmt = this.connection.prepareStatement(sql); - stmt.setInt(1, id); - ResultSet result = stmt.executeQuery(); - return new Student(id, result.getString("name"), result.getString("email_address"), result.getString("github_username"), this.retrievePreferredPartners(id)); - } catch (SQLException e) { - logger.severe("SQL Exception while retrieving Student.\n" + e.getMessage()); - e.printStackTrace(); - return null; - } - } - -} diff --git a/src/main/java/nl/andrewlalis/model/database/DbUtil.java b/src/main/java/nl/andrewlalis/model/database/DbUtil.java new file mode 100644 index 0000000..d58cee0 --- /dev/null +++ b/src/main/java/nl/andrewlalis/model/database/DbUtil.java @@ -0,0 +1,51 @@ +package nl.andrewlalis.model.database; + +import org.hibernate.SessionFactory; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; + +/** + * A utility class for easier interaction with the Hibernate database. + */ +public class DbUtil { + + private static SessionFactory sessionFactory; + + /** + * Set up the session factory based on hibernate.cfg.xml. + */ + private static void setUp() { + final StandardServiceRegistry registry = new StandardServiceRegistryBuilder() + .configure() + .build(); + try { + sessionFactory = new MetadataSources(registry) + .buildMetadata() + .buildSessionFactory(); + } catch (Exception e) { + e.printStackTrace(); + StandardServiceRegistryBuilder.destroy(registry); + } + } + + /** + * Close the session factory when it's no longer needed. + */ + public static void tearDown() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + /** + * Gets the session factory so that sessions can be made. + * @return The session factory. + */ + public static SessionFactory getSessionFactory() { + if (sessionFactory == null) { + setUp(); + } + return sessionFactory; + } +} diff --git a/src/main/java/nl/andrewlalis/model/database/Utils.java b/src/main/java/nl/andrewlalis/model/database/Utils.java deleted file mode 100644 index e908f1d..0000000 --- a/src/main/java/nl/andrewlalis/model/database/Utils.java +++ /dev/null @@ -1,52 +0,0 @@ -package nl.andrewlalis.model.database; - -import nl.andrewlalis.util.FileUtils; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -/** - * Contains some methods which make database actions much easier. - */ -public class Utils { - - /** - * The logger for outputting debug info. - */ - private static final Logger logger = Logger.getLogger(Utils.class.getName()); - static { - logger.setParent(Logger.getGlobal()); - } - - /** - * Gets an ordered list of prepared statements from a file which contains multiple statements separated by a - * semicolon. This method separates those statements into their own strings, and prepares them individually to be - * executed later. - * @param filename The name of the file which contains the statements. - * @param connection The connection to a database; used to prepare statements. - * @return An ordered list of prepared statements which are based on the contents of the file provided. - */ - public static List prepareStatementsFromFile(String filename, Connection connection) { - String string = FileUtils.readStringFromFile(filename); - if (string == null || string.isEmpty()) { - return new ArrayList<>(); - } - String[] splits = string.split(";"); - List statements = new ArrayList<>(); - for (String split : splits) { - if (split.trim().length() > 1) { - try { - statements.add(connection.prepareStatement(split)); - } catch (SQLException e) { - logger.severe("SQLException while preparing a statement:\n" + split + "\nError Code: " + e.getErrorCode() + '\n' + e.getMessage()); - } - } - } - return statements; - } - -} diff --git a/src/main/java/nl/andrewlalis/util/TeamGenerator.java b/src/main/java/nl/andrewlalis/util/TeamGenerator.java index 89b8b35..aa7b426 100644 --- a/src/main/java/nl/andrewlalis/util/TeamGenerator.java +++ b/src/main/java/nl/andrewlalis/util/TeamGenerator.java @@ -2,9 +2,6 @@ package nl.andrewlalis.util; import nl.andrewlalis.model.Student; import nl.andrewlalis.model.StudentTeam; -import nl.andrewlalis.model.error.Error; -import nl.andrewlalis.model.error.Severity; -import nl.andrewlalis.ui.view.InitializerApp; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; @@ -45,9 +42,9 @@ public class TeamGenerator { Iterable records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(new FileReader(filename)); logger.fine("Reading all records into map."); - Map studentMap; + List students; try { - studentMap = readAllStudents(records, teamSize); + students = readAllStudents(records, teamSize); } catch (ArrayIndexOutOfBoundsException e) { logger.severe("StudentTeam size does not match column count in records."); throw new IllegalArgumentException("StudentTeam size does not match column count in records."); @@ -55,7 +52,7 @@ public class TeamGenerator { logger.fine("Generating all valid teams from student map."); - return generateAllValidTeams(studentMap, teamSize); + return generateAllValidTeams(students, teamSize); } /** @@ -71,21 +68,23 @@ public class TeamGenerator { * After all students with preferred partners are placed in teams, the single students are merged, and their teams * are added afterwards. * - * @param studentMap A mapping for each student to their student number. + * @param students A list of students, each with a list of preferred partners. * @param teamSize The preferred maximum size for a team. * @return A list of teams, most of which are of teamSize size. */ - private static List generateAllValidTeams(Map studentMap, int teamSize) { - List singleStudents = new ArrayList<>(studentMap.values()); + private static List generateAllValidTeams(List students, int teamSize) { + List singleStudents = new ArrayList<>(students); List studentTeams = new ArrayList<>(); + // An integer which increments for each valid team. Used to create identifiers for each team. int teamCount = 1; + // For each student, try to make a team from its preferred partners. - for (Map.Entry e : studentMap.entrySet()) { - StudentTeam newTeam = e.getValue().getPreferredTeam(studentMap); + for (Student student : students) { + StudentTeam newTeam = student.getPreferredTeam(); logger.finest("Checking if student's preferred team is valid:\n" + newTeam); // Check if the team is of a valid size, and is not a duplicate. - // Note that at this stage, singles are treated as studentTeams of 1, and thus not valid for any teamSize > 1. + // Note that at this stage, singles are treated as student teams of 1, and thus not valid for any team size > 1. if (newTeam.isValid(teamSize)) { // We know that the team is valid on its own, so now we check if it has members identical to any team already created. boolean matchFound = false; @@ -97,7 +96,7 @@ public class TeamGenerator { } if (!matchFound) { // Once we know this team is completely valid, we remove all the students in it from the list of singles. - newTeam.setId(teamCount++); + newTeam.setNumber(teamCount++); singleStudents.removeAll(Arrays.asList(newTeam.getStudents())); studentTeams.add(newTeam); logger.fine("Created team:\n" + newTeam); @@ -114,14 +113,14 @@ public class TeamGenerator { * size as possible. * @param singleStudents A list of students who have no preferred partners. * @param teamSize The preferred team size. - * @param teamIndex The current number used in assigning an id to the team. + * @param teamIndex The current number used in assigning an number to the team. * @return A list of teams comprising of single students. */ private static List mergeSingleStudents(List singleStudents, int teamSize, int teamIndex) { List studentTeams = new ArrayList<>(); while (!singleStudents.isEmpty()) { StudentTeam t = new StudentTeam(); - t.setId(teamIndex++); + t.setNumber(teamIndex++); logger.fine("Creating new team of single students:\n" + t); while (t.memberCount() < teamSize && !singleStudents.isEmpty()) { Student s = singleStudents.remove(0); @@ -141,8 +140,10 @@ public class TeamGenerator { * @return A map of all students in the file. * @throws ArrayIndexOutOfBoundsException if the teamSize does not work with the columns in the record. */ - private static Map readAllStudents(Iterable records, int teamSize) throws ArrayIndexOutOfBoundsException { + private static List readAllStudents(Iterable records, int teamSize) throws ArrayIndexOutOfBoundsException { Map studentMap = new HashMap<>(); + Map> studentPreferredIds = new HashMap<>(); + // Perform the initial read of the students. for (CSVRecord record : records) { logger.finest("Read record: " + record); List preferredIds = new ArrayList<>(); @@ -152,21 +153,38 @@ public class TeamGenerator { preferredIds.add(Integer.parseInt(record.get(columnOffset + i))); } } - Student s = new Student(Integer.parseInt(record.get(3)), record.get(2), record.get(1), record.get(4), preferredIds); + Student s = new Student(); + s.setNumber(Integer.parseInt(record.get(3))); + s.setName(record.get(2)); + s.setEmailAddress(record.get(1)); + s.setGithubUsername(record.get(4)); if (studentMap.containsValue(s)) { logger.warning("Duplicate entry found for student: " + s + "\nOverwriting previous value."); } studentMap.put(s.getNumber(), s); + studentPreferredIds.put(s, preferredIds); } - // Perform a safety check to ensure all preferred partners are valid students. - for (Map.Entry entry : studentMap.entrySet()) { - // Remove any ids that don't exist in the whole list of students. - entry.getValue().getPreferredPartners().removeIf(partnerId -> !studentMap.containsKey(partnerId)); + // The final list of students, with preferred partners set. + List students = new ArrayList<>(); + + // Assign students to their preferred students. + for (Map.Entry> entry : studentPreferredIds.entrySet()) { + Student s = entry.getKey(); + for (int partnerNumber : entry.getValue()) { + // Check that this preferred partner number exists. + if (studentMap.containsKey(partnerNumber)) { + s.addPreferredPartner(studentMap.get(partnerNumber)); + } else { + logger.warning("Student " + s + " has invalid preferred partner."); + } + } + students.add(s); } + // At this point, all students are valid, and all preferred partners are valid. - logger.fine("Read " + studentMap.size() + " students from records."); - return studentMap; + logger.fine("Read " + students.size() + " students from records."); + return students; } } diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml new file mode 100644 index 0000000..de5ea3a --- /dev/null +++ b/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,30 @@ + + + + + + org.h2.Driver + jdbc:h2:./initializer.h2 + org.hibernate.dialect.H2Dialect + root + root + + + 1 + + + false + + + create-drop + + + + + + + + + \ No newline at end of file