我在静态方法中使用通用类型时遇到了一些麻烦。
欢迎对源代码提出所有意见,尤其是那些显着改进代码的意见。我目前也不打算使用任何外部框架,除了 JDBC,以保持简单,请不要过分强调这一点。
我将在数据库上使用的操作非常少,这一事实也支持我不使用外部框架的观点:
- 插入数据
- 更新数据
- 检索所有 字段。 (只需输入不同的 SQL 查询,您就可以选择要检索的字段
我不打算制作一个完整的框架,所以我知道它不会支持所有内容。检索所有字段的速度既不是真正的问题,因为这几乎只在服务器启动时完成,如果在任何其他时间使用,它将在后台任务中完成,我并不关心它何时完成.
实体.java:
abstract public class Entity<KeyType, DataType> {
protected KeyType key;
protected List<Object> data;
public Entity() {
data = new ArrayList<>();
}
//abstract public static Map<KeyType, DataType> getAll();
protected List<Object> createData(final DataAction dataAction) {
List<Object> list = new ArrayList<>();
if (dataAction == DataAction.INSERT) {
list.add(key);
}
list.addAll(data);
if (dataAction == DataAction.UPDATE) {
list.add(key);
}
return list;
}
abstract public void insert();
abstract public void update();
protected static <KeyType, DataType> Map<KeyType, DataType> getData(final Class<DataType> dataTypeClass, final String query) {
Map<KeyType, DataType> map = new HashMap<>();
try {
PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
KeyType key = (KeyType)resultSet.getObject(1);
int index = 2;
List<Object> dataList = new ArrayList<>();
while (resultSet.getObject(index) != null) {
dataList.add(resultSet.getObject(index));
index++;
}
DataType dataObject = null;
try {
dataObject = dataTypeClass.getConstructor(List.class).newInstance(dataList);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
}
map.put(key, dataObject);
}
} catch (SQLException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
}
return map;
}
protected void executeQuery(final String query, final List<Object> data) {
try {
PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(query);
int dataIndex = 0;
for (Object dataObject : data) {
preparedStatement.setObject(dataIndex, dataObject);
dataIndex++;
}
preparedStatement.execute();
preparedStatement.close();
} catch (SQLException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
具体实现,Account.java:
public class Account extends Entity<String, Account> {
private final static String SELECT_ALL_QUERY = "SELECT * FROM accounts";
private final static String INSERT_QUERY = "INSERT INTO accounts (username, password) VALUES(?, ?)";
private final static String UPDATE_QUERY = "UPDATE accounts SET password=? WHERE username=?";
private String username;
private String password;
public Account(final String username, final String password) {
this.username = username;
this.password = password;
key = username;
data.add(password);
}
public Account(final List<Object> data) {
this((String)data.get(0), (String)data.get(1));
}
public String getUsername() {
return username;
}
public void setUsername(final String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
public static Map<String, Account> selectAll() {
return getData(Account.class, SELECT_ALL_QUERY);
}
@Override
public void insert() {
executeQuery(INSERT_QUERY, createData(DataAction.INSERT));
}
@Override
public void update() {
executeQuery(UPDATE_QUERY, createData(DataAction.UPDATE));
}
}
我对具体实现总体上很满意,似乎我已经设法将其降低到最低限度,除了 public Account(final List<Object> data)
看起来不太好,但我可以忍受。
但是,正如猜测的那样,getData()
来自 Entity
肯定不是很好,如果可能我想改进它。
我想使用类似 DataType dataObject = new DataType(dataList)
的东西, 但似乎无法实例化通用类型参数。
那么在我当前的 View 中有什么方法可以优化我当前的代码吗?是否可以进一步解耦具体类和抽象类?
编辑:
添加了一个相关问题(我认为我不应该为此提出一个全新的问题,对吧?):
有没有办法移动静态字符串(SQL 查询)和 insert()
和 update()
从 Account 类出来,进入 Entity 类?
最佳答案
为了避免在您的 getData
方法中使用反射,您应该接受一个工厂,它给定一个 ResultSet
创建特定类型的实例。您的 selectAll
方法将类似于:
public static Map<String, Account> selectAll()
{
return getData(
new EntityFactory<Account>()
{
public Account newInstance(ResultSet resultSet) throws SQLException
{
return new Account(resultSet.getString(0), resultSet.getString(1));
}
},
SELECT_ALL_QUERY
);
}
getData
方法的结果如下:
protected static <K, T extends Entity<K>> Map<K, T> getData(EntityFactory<T> entityFactory, String query)
{
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try
{
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(query);
resultSet = preparedStatement.executeQuery();
Map<K, T> entities = new HashMap<>();
while (resultSet.next())
{
Entity<K> entity = entityFactory.newInstance(resultSet);
entities.put(entity.getKey(), entity);
}
return entities;
}
finally
{
closeQuietly(resultSet);
closeQuietly(prepareStatement);
closeQuietly(connection);
}
}
并假设 Entity
看起来像:
public interface Entity<K>
{
public K getKey();
}
这允许您删除反射并将了解数据库结构的代码保留在一个地方。在执行插入和更新时,您还应该使用类似的模板模式从域对象映射到准备好的语句。
现在您已经征求了对代码的总体意见。
首先,像这样的代码违反了 Single Responsibility Principal和 Seperation Of Concerns .域类应该是域类并且不包含持久性逻辑。查看类似 Data Access Object 的模式了解如何做到这一点。
其次,虽然我完全支持保持简单,Hibernate很久以前就解决了这个问题 JPA对其进行标准化 - 您需要一个很好的理由不使用其中一个或两个 API。
最后,您对数据库资源的使用——如果您打算直接使用 JDBC,则必须正确清理。数据库连接是昂贵的资源,应该这样处理,任何 JDBC 调用的基本模板应该是:
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try
{
connection = //get connection from pool or single instance.
preparedStatement = connection.prepareStatement("SELECT * FROM table WHERE column = ?");
preparedStatement.setString(1, "some string");
resultSet = preparedStatement.executeQuery();
while (resultSet.next())
{
//logic goes here.
}
}
catch (SQLException e)
{
//Handle exceptions.
}
finally
{
closeQuietly(resultSet);
closeQuietly(prepareStatement);
closeQuietly(connection);
}
closeQuietly
方法必须重载但应采用一般形式:
try
{
if (resultSet != null)
{
resultSet.close();
}
}
catch (SQLException e)
{
//Log exceptions but don't re-throw.
}
关于Java 数据实体模型 : Constructing general types,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16547626/