1
\$\begingroup\$

I am trying to make some changes to the ORM structure at my current organization. Please provide any reviews/feedback, greatly appreciated.

Problem: I want to encapsulate database access logic out of User.java. User object has properties like address1, address2, city which should be combined into an Address.java object encapsulating its behavior and move that logic out of User.java. The mapping of Address object to database columns in sql statement should be easy to handle.

Our current ORM is something like this: UserDBMapper.java: This file has instance variables mapping to the database . This file is used to generate BaseUser.java.

public class UserDBMapper {
    public static Map<String, String> getTableName() {
        return "user";
    }

    public Map<String, String> getColumnMap() {
        Map<String, String> columns = new HashMap<String, String>();
        columns.put("id", "id");
        columns.put("emailAddress", "email_address");
        columns.put("address1", "address1");
        columns.put("address2", "address2");
        columns.put("city", "city");
    }

    public long id;
    public String emailAddress;   
    public String address1;
    public String addres2;
    public String city;
}

BaseUser.java: This file is auto generated and has methods to create object from ResultSet, bind object to PreparedStatement, select Sql, getter/setter for all instance variables in UserDBMapper, etc.:

public class BaseUser extends UserDBMapper {
    public void setId(long id) {this.id = id;}
    public long getId() {return this.id;}
    //getter setter for all the properties in UserDBMapper

    public static User buildObject(ResultSet rs ) throws Exception {
        User obj = new User();    
        obj.setId(rs.getLong("id"));    
        obj.setEmailAddress(rs.getString("email_address"));    
        //similarly set all properties

        return obj;
    }

    public void prepareStatement(PreparedStatement ps, BaseUser baseUser) throws SQLException {
        ps.setLong(1, baseuser.getId());
        ps.setString(2, data.getEmailAddress());
        //Similarly set all properties in preparedStatement
    }

    public void update(Connection conn) throws Exception {
        PreparedStatement stmt = null;
        Object rs = null;

        try {
            stmt = conn.prepareStatement(getUpdateSQL() + " where id = ?");
            this.bind(stmt);
            stmt.executeUpdate();
        } catch (SQLException var9) {} finally {}
        //I have removed the code for handling errors etc. to keep it simple
    }

    //Not providing method implementations to keep it short/simple
    public static String getUpdateSQL()
    public static String getSelectSQL()
    public static String getTableName()
    public static String getSelectSQL()
}

User.java: All the business logic goes here as well as any custom sql queries other than simple update, select by id etc.

public class User extends BaseUser {
    //business logic
    public boolean isPaidUser()
    public Date getLicenseExpiryDate()
}

Solution I am thinking of:

UserDBProperties: //This class has getter for properties mapping to database columns. It also enforces constructor signature to be used in User.java, If a new column is added or deleted, there should not be properties mismatch.

public class UserDBProperties {
    protected long getId();
    protected String getEmailAddress();
    protected String getAddress1();
    protected String getAddress2();
    protected String getCity();

    public UserDBProperties(long id, String email, String addr1, String addr2, String city) {
        //do nothing
    }
}

UserDBMapper: This file will be autogenerated. This has methods to create object from ResultSet and set object properties in preparedStatement.

public class UserDBMapper implements DBMapper {
    @Override
    public String getTableName() {
        return "user";
    }

    @Override
    public String getUpdateSQL() {
        return "UPDATE user set id=? , email_address=?, address1= ?  where ";
    }

    @Override
    public String getSelectSQL(String prefix, boolean distinct){ /*implementation not added*/}
    private String getFromSQL(String alias) {
        return " FROM " + getTableName() + " " + (alias == null ? "" : alias) + " ";
    }

    @Override
    public String getInsertSQL(){ /*implementation not added*/}

    @Override
    public void prepareStatement(UserDBProperties table, PreparedStatement ps) throws SQLException {
        ps.setLong(1, table.getId());
        ps.setString(2, table.getEmailAddress());
        ps.setString(3, table.getAddress1());
        ps.setString(4, table.getAddress2());
        ps.setString(5, table.city());
    }

    @Override
    public User buildObject(ResultSet rs) throws SQLException {
        return new User(rs.getLong("id"),
                rs.getString("email_address"),
                rs.getString("address1"),
                rs.getLong("address2"),
                rs.getLong("city"));
    }
}

User.java: This class extends UserProperties.java and implements all the getters as well as matching constructor.

public class User extends UserProperties {
    private long id;
    private String emailAddress;
    private Address address;

    @Override
    public long getId() {
        return this.id;
    }

    @Override
    public String getEmailAddress() {
        return this.emailAddress;
    }

    @Override
    public String getAddress1() {
        return this.address.getLine1();
    }

    @Override
    public String getAddress2() {
        return this.address.getLine2();
    }

    public User(long id, String email, String adr1, String adr2, String city) {
        this.id = id;
        this.emailAddress = email;
        this.address = new Address(adr1, adr2, city);
    }
}

UserRepository.java: This class will have all the standard and custom insert/update/delete SQL queries.

public class UserRepository implements Repository {
    public abstract class Repository<O extends Table, M extends RowMapper> {
    protected UserMapper getMapperInstance() {
        return new UserMapper();
    }

    protected void save(User user) {
        if (user.getId() > 0) {
            update(user);
        } else {
            insert(user);
        }
    }

    protected void update(O entity) {
        final UserMapper rowMapper = getMapperInstance();
        PreparedUpdateCommand command = new PreparedUpdateCommand() {
            public void bind(PreparedStatement ps) throws Exception {
                rowMapper.prepareStatement(entity, ps);
            }
        };

        command.execute(rowMapper.getUpdateSQL() + " id = " + entity.getId());
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.