Task description.
import javax.sql.DataSource;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
// 1st part
interface ORManager {
// let it work with ids:
// - Long (autogenerated at DB side) (HIGH)
// - UUID (autogenerated at ORM side) (MEDIUM)
// - String (OPTIONAL)
// The fields may be of types:
// - int/Integer (HIGH)
// - long/Long (HIGH)
// - double/Double (OPTIONAL)
// - boolean/Boolean (OPTIONAL)
// - String (HIGH)
// - LocalDate (MEDIUM)
// - LocalTime (MEDIUM)
// - LocalDateTime/Instant (MEDIUM)
// - BigDecimal (OPTIONAL)
// - Enum + (OPTIONAL)
// @Enumerated(EnumType.ORDINAL/EnumType.STRING)
// initialize connection factory for the DB
// read the jdbc url, username and password from
// the given property file
static ORManager withPropertiesFrom(String filename) {
return null; // todo
}
// initialize connection factory for the DB based on the DataSource
static ORManager withDataSource(DataSource dataSource) {
return null; // todo
}
// generate the schema in the DB
// for given list of entity classes (and all related
// by OneToMany/ManyToOne) create a schema in DB
void register(Class... entityClasses);
// CREATE
// save a new object to DB, set id if autogenerated
// or merge into DB if id is present
Object save(Object o);
// save a new object to DB, set id if autogenerated
// throw if the object has id already set (except for String)
void persist(Object o);
// READ
<T> Optional<T> findById(Serializable id, Class<T> cls);
// READ ALL
<T> List<T> findAll(Class<T> cls);
// READ ALL LAZY
<T> Iterable<T> findAllAsIterable(Class<T> cls); // (MEDIUM)
<T> Stream<T> findAllAsStream(Class<T> cls); // (OPTIONAL)
// UPDATE
<T> T merge(T o); // send o -> DB row (to table)
<T> T update(T o); // send o <- DB row (from table)
// DELETE
// set autogenerated id to null
// return true if successfully deleted
boolean delete(Object o);
}
// todo: declare properly the annotations
// 1st part
@interface Entity {} // (HIGH)
@interface Id {} // (HIGH)
@interface Table {String value() default "";} // (HIGH)
@interface Column {String value() default "";}// (HIGH)
// 2nd part
@interface OneToMany {String mappedBy() default "";} // (MEDIUM)
@interface ManyToOne {String columnName() default "";} // (MEDIUM)
// 1st part
class Main {
public static void main(String[] args) {
String propertiesFilename = "";
ORManager ormManager = ORManager.withPropertiesFrom(
propertiesFilename
);
ormManager.register(Book.class, Publisher.class);
var publisher = new Publisher("MyPub");
ormManager.persist(publisher);
var book1 = new Book("Solaris", LocalDate.of(1961, 1, 1));
ormManager.persist(book1);
book1.setPublisher(publisher);
ormManager.merge(book1);
// ...
}
}
@Entity
@Table("books")
class Book {
@Id
private Long id;
private String title;
@Column("published_at")
private LocalDate publishedAt;
public Book() {
}
public Book(String title, LocalDate publishedAt) {
this.title = title;
this.publishedAt = publishedAt;
}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public LocalDate getPublishedAt() {
return publishedAt;
}
public void setPublishedAt(LocalDate publishedAt) {
this.publishedAt = publishedAt;
}
// 2nd stage:
@ManyToOne(columnName = "publisher_id")
Publisher publisher = null;
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
}
@Entity
class Publisher {
@Id
private Long id;
private String name;
public Publisher() {
}
public Publisher(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 2nd stage
@OneToMany(mappedBy = "publisher")
private List<Book> books = new ArrayList<>();
public List<Book> getBooks() {
return books;
}
}