Commit 831359c5 authored by Arne Styve's avatar Arne Styve
Browse files

Merge branch 'feature/TEST-ASTY' into develop

parents 3cd42ed9 d2dd4258
/out/
addressbook.dat
.idea/sonarlint
/vpproject/
/Contacts.vpdm/
/derby.log
/contactsdb/
\ No newline at end of file
<component name="libraryTable">
<library name="libs">
<CLASSES>
<root url="file://$PROJECT_DIR$/libs" />
</CLASSES>
<JAVADOC />
<SOURCES />
<jarDirectory url="file://$PROJECT_DIR$/libs" recursive="false" />
</library>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="jpa" name="JPA">
<configuration>
<setting name="validation-enabled" value="true" />
<datasource-mapping>
<factory-entry name="contacts-pu" />
</datasource-mapping>
<naming-strategy-map />
<deploymentDescriptor name="persistence.xml" url="file://$MODULE_DIR$/src/META-INF/persistence.xml" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
......@@ -7,5 +19,6 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="libs" level="project" />
</component>
</module>
\ No newline at end of file
......@@ -12,10 +12,30 @@ The project was developed for use in teaching in the course "IDATx2001 Programme
**Version** | **Description**
--------|------------
v0.4 | Added Relational Database support, using the embedded Apache Derby server. For details of the changes made, se below.
v0.3 | Adds object serialization of the entire address book.
v0.2 | Added import and export from/to CSV-file. A default CSV-file is provided (Addresses.csv)
v0.1 | First release with basic add, edit, delete functionality.
### Release v0.4
The following changes were made to enable support for a Relational Database server (using the **embedded** mode of
the [Apache Derby RDBMS](https://db.apache.org/derby/), using the [EclipseLink](https://www.eclipse.org/eclipselink/)
persistence provider (JPA):
* **Added libraries** - Added a new folder, called *"libs"* in the IntelliJ project. Copied the **eclipselink.jar**
and the **jakarta.persistence_2.2.3.jar** files to the *libs*-folder for the **EclipseLink**-JPA support. Added
**derby.jar** to the *libs*-folder for the Apache Derby embedded support.
* **persistence.xml** - Added this file (used by the JPA-framework EclipseLink) to a folder named "**META-INF**", which
is the same folder that the **MANIFEST.MF** should be.
* **ContactDetails**-class: Added ORM/JPA specific annotations, like *@Entity*, *@Id* etc. Also, it must provide a
default constructor with no parameters.
* **AddressBook** - interface: Added an Interface named *AddressBook*, renamed the exsisting *AddressBook* to
*AddressBookPlain*. (Used the IntelliJ *Refactoring*: Extract Interface..)
* Added a new class **AddressBookDAO** which implements the interface AddressBook. This is the **Data Access Object**
(DAO) providing the interface to the database. The class maps the methods defined in the interface to database
actions.
## Description of the project
The project is made to demonstrate a typical application with a graphical user interface (GUI) implemented in JavaFX.
It's a classic address book example, where you can create contacts to be added to the address book, edit existing
......
File added
Manifest-Version: 1.0
Created-By: 1.8.0_201 (Oracle Corporation)
Main-Class: no.ntnu.idata2001.contacts.views.ContactsApp
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="contacts-pu" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:derby:contactsdb;create=true"/>
<property name="javax.persistence.jdbc.user" value="app"/>
<property name="javax.persistence.jdbc.password" value="app"/>
<property name="eclipselink.target-database" value="Derby"/>
<!-- Alternatives: create-tables, drop-and-create-tables-->
<property name="eclipselink.ddl-generation" value="create-tables"/>
<!-- Alternatives: FINE (logs all SQL), ALL, CONFIG, INFO, WARNING..., OFF -->
<property name="eclipselink.logging.level" value="FINE"/>
<!--property name="javax.persistence.sql-load-script-source" value="META-INF/sql/data.sql"/-->
</properties>
</persistence-unit>
</persistence>
\ No newline at end of file
INSERT INTO CONTACTDETAILS(Id, Name, Phone, Address) VALUES(1, 'Jan Jensen', '(+47) 342 32 456', 'Gate 5, 5700 Voss')
INSERT INTO CONTACTDETAILS(Id, Name, Phone, Address) VALUES(2, 'Ola Jensen', '(+47) 342 32 456', 'Gate 5, 5700 Voss')
INSERT INTO CONTACTDETAILS(Id, Name, Phone, Address) VALUES(3, 'Per Jensen', '(+47) 342 32 456', 'Gate 5, 5700 Voss')
\ No newline at end of file
......@@ -19,6 +19,7 @@ import javafx.scene.layout.GridPane;
import javafx.stage.FileChooser;
import javafx.util.Pair;
import no.ntnu.idata2001.contacts.model.AddressBook;
import no.ntnu.idata2001.contacts.model.AddressBookDAO;
import no.ntnu.idata2001.contacts.model.AddressBookFileHandler;
import no.ntnu.idata2001.contacts.model.ContactDetails;
import no.ntnu.idata2001.contacts.views.ContactDetailsDialog;
......@@ -205,6 +206,15 @@ public class MainController {
return AddressBookFileHandler.loadFromFile(inFile);
}
/**
* Loads an entire AddressBook from a database.
*
* @return an address book populated by contact details loaded from the database.
*/
public AddressBook loadAddressBookFromDB() {
return new AddressBookDAO();
}
/**
* Exit the application. Displays a confirmation dialog.
*/
......
......@@ -3,35 +3,8 @@ package no.ntnu.idata2001.contacts.model;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeMap;
/**
* Represents an Address book containing contacts with contact details.
* Based on the example in the book "Objects first with Java" by David J. Barnes
* and Michael Kölling.
*
* <p>Each contact is stored in a TreeMap using the phone number as the key.
*
* @author David J. Barnes and Michael Kölling and Arne Styve
* @version 2020.03.16
*/
public class AddressBook implements Serializable, Iterable<ContactDetails> {
// Storage for an arbitrary number of details.
// We have chosen to use TreeMap instead of HashMap in this example, the
// main difference being that a TreeMap is sorted. That is, the keys are sorted,
// so when retrieving an Iterator from a TreeMap-collection, the iterator will
// iterate in a sorted manner, which wil not be the case for a HashMap.
// TreeMap is a bit less efficient than a HashMap in terms of searching, du to the
// sorted order. For more details on the difference:
// https://javatutorial.net/difference-between-hashmap-and-treemap-in-java
private final TreeMap<String, ContactDetails> book;
/**
* Creates an instance of the AddressBook, initialising the instance.
*/
public AddressBook() {
book = new TreeMap<>();
}
public interface AddressBook extends Serializable, Iterable<ContactDetails> {
/**
* Searches for a contact matching the phone number given by the parameter.
......@@ -41,9 +14,7 @@ public class AddressBook implements Serializable, Iterable<ContactDetails> {
* @param phoneNumber The number to be looked up.
* @return The details corresponding to the phone number.
*/
public ContactDetails findContactByPhoneNumber(String phoneNumber) {
return book.get(phoneNumber);
}
ContactDetails findContactByPhoneNumber(String phoneNumber);
/**
* Return whether or not the current phone number is in use.
......@@ -51,20 +22,14 @@ public class AddressBook implements Serializable, Iterable<ContactDetails> {
* @param phoneNumber The name or phone number to be looked up.
* @return true if the phoneNumber is in use, false otherwise.
*/
public boolean keyInUse(String phoneNumber) {
return book.containsKey(phoneNumber);
}
boolean keyInUse(String phoneNumber);
/**
* Add a new contact to the address book.
*
* @param contact The contact to be added.
*/
public void addContact(ContactDetails contact) {
if (contact != null) {
book.put(contact.getPhone(), contact);
}
}
void addContact(ContactDetails contact);
/**
* Change the contact previously stored under the given key.
......@@ -73,13 +38,8 @@ public class AddressBook implements Serializable, Iterable<ContactDetails> {
* This should be a key that is currently in use.
* @param contact The replacement contact.
*/
public void changeDetails(String oldKey,
ContactDetails contact) {
if (keyInUse(oldKey) && contact != null) {
removeContact(oldKey);
addContact(contact);
}
}
void changeDetails(String oldKey,
ContactDetails contact);
/**
* Return the number of entries currently in the
......@@ -87,9 +47,7 @@ public class AddressBook implements Serializable, Iterable<ContactDetails> {
*
* @return The number of entries.
*/
public int getNumberOfEntries() {
return this.book.size();
}
int getNumberOfEntries();
/**
* Remove the contact with the given phone number from the address book.
......@@ -97,23 +55,20 @@ public class AddressBook implements Serializable, Iterable<ContactDetails> {
*
* @param phoneNumber The phone number to the contact to remove
*/
public void removeContact(String phoneNumber) {
if (keyInUse(phoneNumber)) {
this.book.remove(phoneNumber);
}
}
void removeContact(String phoneNumber);
/**
* Returns all the contacts as a collection.
*
* @return all the contacts as a collection.
*/
public Collection<ContactDetails> getAllContacts() {
return this.book.values();
}
Collection<ContactDetails> getAllContacts();
/**
* Close the data source.
*/
void close();
@Override
public Iterator<ContactDetails> iterator() {
return this.book.values().iterator();
}
Iterator<ContactDetails> iterator();
}
package no.ntnu.idata2001.contacts.model;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class AddressBookDAO implements AddressBook {
// Note the 'transient'-keyword. This is to indicate that these fields
// should never be serialized. Since the AddressBook-interface
// implements Serializable (used for object serialisation), this class
// to is tagged to be serializable. But the two fields in this class is used to
// communicate with the database, and should hence not be serialized. Neither are
// the classes EntityManager nor EntityManagerFactory serializable.
private final transient EntityManagerFactory efact;
private final transient EntityManager eman;
/**
* Creates an instance of the AddressbookDAO.
*/
public AddressBookDAO() {
this.efact = Persistence.createEntityManagerFactory("contacts-pu");
this.eman = efact.createEntityManager();
}
@Override
public ContactDetails findContactByPhoneNumber(String phoneNumber) {
return null;
}
@Override
public boolean keyInUse(String phoneNumber) {
return false;
}
@Override
public void addContact(ContactDetails contact) {
this.eman.getTransaction().begin();
this.eman.persist(contact);
this.eman.getTransaction().commit();
}
@Override
public void changeDetails(String oldKey, ContactDetails contact) {
//TODO: To be implemented later....
}
@Override
public int getNumberOfEntries() {
return this.getAllContacts().size();
}
@Override
public void removeContact(String phoneNumber) {
//TODO: To be implemented later...
}
@Override
public Collection<ContactDetails> getAllContacts() {
List<ContactDetails> contactsList = null;
String sql = "SELECT c FROM ContactDetails c";
Query query = eman.createQuery(sql);
contactsList = query.getResultList();
return contactsList;
}
@Override
public void close() {
this.eman.close();
this.efact.close();
}
@Override
public Iterator<ContactDetails> iterator() {
return getAllContacts().iterator();
}
}
......@@ -154,7 +154,7 @@ public class AddressBookFileHandler {
} catch (IOException | ClassNotFoundException e) {
logger.log(Level.INFO, "Could not open file "
+ inFile.getName() + ". An empty AddressBook was returned.");
return new AddressBook();
return new AddressBookPlain();
}
}
}
package no.ntnu.idata2001.contacts.model;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeMap;
/**
* Represents an Address book containing contacts with contact details.
* Based on the example in the book "Objects first with Java" by David J. Barnes
* and Michael Kölling.
*
* <p>Each contact is stored in a TreeMap using the phone number as the key.
*
* @author David J. Barnes and Michael Kölling and Arne Styve
* @version 2020.03.16
*/
public class AddressBookPlain implements AddressBook {
// Storage for an arbitrary number of details.
// We have chosen to use TreeMap instead of HashMap in this example, the
// main difference being that a TreeMap is sorted. That is, the keys are sorted,
// so when retrieving an Iterator from a TreeMap-collection, the iterator will
// iterate in a sorted manner, which wil not be the case for a HashMap.
// TreeMap is a bit less efficient than a HashMap in terms of searching, du to the
// sorted order. For more details on the difference:
// https://javatutorial.net/difference-between-hashmap-and-treemap-in-java
private final TreeMap<String, ContactDetails> book;
/**
* Creates an instance of the AddressBook, initialising the instance.
*/
public AddressBookPlain() {
book = new TreeMap<>();
}
@Override
public ContactDetails findContactByPhoneNumber(String phoneNumber) {
return book.get(phoneNumber);
}
@Override
public boolean keyInUse(String phoneNumber) {
return book.containsKey(phoneNumber);
}
@Override
public void addContact(ContactDetails contact) {
if (contact != null) {
book.put(contact.getPhone(), contact);
}
}
@Override
public void changeDetails(String oldKey,
ContactDetails contact) {
if (keyInUse(oldKey) && contact != null) {
removeContact(oldKey);
addContact(contact);
}
}
@Override
public int getNumberOfEntries() {
return this.book.size();
}
@Override
public void removeContact(String phoneNumber) {
if (keyInUse(phoneNumber)) {
this.book.remove(phoneNumber);
}
}
@Override
public Collection<ContactDetails> getAllContacts() {
return this.book.values();
}
@Override
public void close() {
// Nothing needed to be done. Intentionally left empty.
}
@Override
public Iterator<ContactDetails> iterator() {
return this.book.values().iterator();
}
}
package no.ntnu.idata2001.contacts.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* Holds details about a contact, like name, address and phone number.
......@@ -10,7 +13,11 @@ import java.io.Serializable;
* @author David J. Barnes and Michael Kölling and Arne Styve
* @version 2020.03.16
*/
@Entity
public class ContactDetails implements Comparable<ContactDetails>, Serializable {
@Id
@GeneratedValue
private Integer id; // A unique ID
private String name;
private String phone;
private String address;
......@@ -39,6 +46,14 @@ public class ContactDetails implements Comparable<ContactDetails>, Serializable
this.address = address.trim();
}
/**
* Default constructor. Required according to the Java Beans standard,
* required by JPA.
*/
public ContactDetails() {
// Intentionally left empty.
}
/**
* Returns the name.
*
......
......@@ -61,7 +61,8 @@ public class ContactsApp extends Application {
this.mainController = new MainController();
// Initialise the Address Book from a file
this.addressBook = this.mainController.loadAddressBookFromFile();
//this.addressBook = this.mainController.loadAddressBookFromFile();
this.addressBook = this.mainController.loadAddressBookFromDB();
}
@Override
......@@ -105,7 +106,7 @@ public class ContactsApp extends Application {
@Override
public void stop() {
// Save the address book to file
this.mainController.saveAddressBookToFile(this.addressBook);
//this.mainController.saveAddressBookToFile(this.addressBook);
// Exit the application
System.exit(0);
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment