I taught a course years ago, and then I used some JDBC exercises using what I now know is the Active Record Pattern.
I would like to modernize the exercise by changing the Active Record Pattern so classes don't do their own persistence. But I would not like to use any persistence or ORM framework for teaching purposes.
A sample class is Vehicle:
- It receives a Connection and plate number in the constructor.
- In the constructor it queries the database and populates the class members and a boolean member
existInDB
is set to true. - If the plate is not found in the DB,
existInDB
remains false. - You can change the state of the object with setters.
- If you call
save()
it either inserts in the database ifexistInDB
is false or updates a row in the database ifexistInDB
is false. - After refactoring the class would not be a simple data transfer object because it has a
doSomeBusinessThing()
method that does some business thing. - All other classes like Maker, Owner, also use the Active Record Pattern, but we can leave them alone for now.
This is not production code. It's Java SE course material. I don't want to introduce frameworks to people who are learning the language and/or OOP principles for the first time. One requirement of my question is that it must be POJO-based. Maybe using a design pattern but no external frameworks or ORM.
I would like to read your advice on how to do such a refactoring.
The vehicle class:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Vehicle {
private String plate;
private Model model;
private Owner owner;
private String color;
private String status;
private int year;
private boolean existInDB= false;
private Connection conn;
private ResultSet rs;
public Vehicle(Connection con_,String plate_) throws SQLException {
String sql=
"select \n"+
" v.plate, \n" +
" v.model_id, \n"+
" v.owner_id, \n"+
" v.color, \n"+
" v.status, \n"+
" v.year \n"+
"from \n"+
" vehicle v \n"+
"where \n"+
" v.plate = ? \n";
PreparedStatement ps = con_.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
ps.setString(1, plate_);
rs = ps.executeQuery();
plate = plate_;
conn = con_;
if (rs.next()){
try {
model = new Model(conn,rs.getString("model_id"));
} catch (UnknownMakerException e) {
e.printStackTrace();
} catch (UnknownModelException e) {
e.printStackTrace();
}
try {
owner = new Owner(conn,rs.getInt("owner_id"));
} catch (UnknownOwnerException e) {
e.printStackTrace();
}
color = rs.getString("color");
status = rs.getString("status");
year = rs.getInt("year");
existInDB = true;
}
}
public void save() throws SQLException{
if (existsInDB()){
rs.absolute(1);
rs.updateString("plate",plate);
rs.updateString("model_id", model.getID());
rs.updateInt("owner_id", owner.getID());
rs.updateString("color", color);
rs.updateString("status", status);
rs.updateInt("year", year);
rs.updateRow();
} else {
rs.moveToInsertRow();
rs.updateString("plate", plate);
rs.updateString("model_id", model.getID());
rs.updateInt("owner_id", owner.getID());
rs.updateString("color", color);
rs.updateString("status", status);
rs.updateInt("year", year);
rs.insertRow();
}
}
public String getPlate() {
return plate;
}
public Model getModel() {
return model;
}
public Owner getOwner() {
return owner;
}
public String getColor() {
return color;
}
public String getStatus() {
return status;
}
public int getYear() {
return year;
}
public boolean existsInDB() {
return existInDB;
}
public void setPlate(String plate) {
this.plate = plate;
}
public void setModel(String modelID_) throws SQLException, UnknownModelException {
try {
this.model = new Model(conn,modelID_);
} catch (UnknownMakerException e) {
e.printStackTrace();
}
}
public void setOwner(int ownerID) throws SQLException, UnknownOwnerException {
this.owner = new Owner(conn,ownerID);
}
public void setColor(String color) {
this.color = color;
}
public void setStatus(String status) {
this.status = status;
}
public void setYear(int year) {
this.year = year;
}
protected void finalize() throws Throwable, SQLException
{
try {
PreparedStatement stmt = (PreparedStatement) rs.getStatement();
rs.close();
stmt.close();
stmt = null;
} finally {
super.finalize();
}
}
public String getDescription(){
return
this.getModel().getMaker().getName() + " " +
this.getModel().getName() + " " +
this.getColor() + " " +
this.getYear();
}
public String toString(){
return
"\nVEHICLE" +
"\nPlate: " +this.getPlate() +
"\nModel ID: " +this.getModel().getID() +
"\nOwner ID: " +this.getOwner().getID() +
"\nColor: " +this.getColor() +
"\nStatus: " +this.getStatus() +
"\nYear: " +this.getYear();
}
public void doSomeBusinessThing(){
/* some important business logic just to clarify
* that this is not a simple data transfer object
*/
}
}