JDBC学习(六)JDBC数据源的简单实现(使用代理模式proxy pattern)

本文主要探讨了ArrayList和LinkedList这两种Java集合类之间的区别。ArrayList基于数组实现,因此在访问元素方面表现出较高的效率;而LinkedList则采用链表结构,使得插入和删除操作更为高效。
<p>1,ArrayList 和linkedList的区别。ArrayList使用数组实现,访问数组很快,LinkedList使用链表来实现,增加删除速度很快。</p>

<p><textarea cols="67" rows="15" name="code" class="java">package cn.itcast.jdbc.datasource;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Map;

/**
*
* 2008-12-13
*
* @author <a href="mailto:liyongibm@gmail.com" mce_href="mailto:liyongibm@gmail.com">liyong</a>
*
*/
public class MyConnection implements Connection {
private Connection realConnection;
private MyDataSource2 dataSource;
private int maxUseCount = 5;
private int currentUserCount = 0;

MyConnection(Connection connection, MyDataSource2 dataSource) {
this.realConnection = connection;
this.dataSource = dataSource;
}

public void clearWarnings() throws SQLException {
this.realConnection.clearWarnings();
}

public void close() throws SQLException {
this.currentUserCount++;
if (this.currentUserCount < this.maxUseCount)
this.dataSource.connectionsPool.addLast(this);
else {
this.realConnection.close();
this.dataSource.currentCount--;
}
}

public void commit() throws SQLException {
this.realConnection.commit();
}

public Statement createStatement() throws SQLException {
return this.realConnection.createStatement();
}

public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public Statement createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public boolean getAutoCommit() throws SQLException {
// TODO Auto-generated method stub
return false;
}

public String getCatalog() throws SQLException {
// TODO Auto-generated method stub
return null;
}

public int getHoldability() throws SQLException {
// TODO Auto-generated method stub
return 0;
}

public DatabaseMetaData getMetaData() throws SQLException {
// TODO Auto-generated method stub
return null;
}

public int getTransactionIsolation() throws SQLException {
// TODO Auto-generated method stub
return 0;
}

public Map<String, Class<?>> getTypeMap() throws SQLException {
// TODO Auto-generated method stub
return null;
}

public SQLWarning getWarnings() throws SQLException {
// TODO Auto-generated method stub
return null;
}

public boolean isClosed() throws SQLException {
// TODO Auto-generated method stub
return false;
}

public boolean isReadOnly() throws SQLException {
// TODO Auto-generated method stub
return false;
}

public String nativeSQL(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}

public CallableStatement prepareCall(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}

public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
}

public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public void releaseSavepoint(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub

}

public void rollback() throws SQLException {
// TODO Auto-generated method stub

}

public void rollback(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub

}

public void setAutoCommit(boolean autoCommit) throws SQLException {
// TODO Auto-generated method stub

}

public void setCatalog(String catalog) throws SQLException {
// TODO Auto-generated method stub

}

public void setHoldability(int holdability) throws SQLException {
// TODO Auto-generated method stub

}

public void setReadOnly(boolean readOnly) throws SQLException {
// TODO Auto-generated method stub

}

public Savepoint setSavepoint() throws SQLException {
// TODO Auto-generated method stub
return null;
}

public Savepoint setSavepoint(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
}

public void setTransactionIsolation(int level) throws SQLException {
// TODO Auto-generated method stub

}

public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
// TODO Auto-generated method stub

}

}
</textarea></p>

<p><textarea cols="67" rows="15" name="code" class="java">package cn.itcast.jdbc.datasource;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

import javax.sql.DataSource;

/**
*
* 2008-12-13
*
* @author <a href="mailto:liyongibm@gmail.com" mce_href="mailto:liyongibm@gmail.com">liyong</a>
*
*/
public class MyDataSource2 implements DataSource{
private static String url = "jdbc:mysql://localhost:3306/jdbc";
private static String user = "root";
private static String password = "";

private static int initCount = 1;
private static int maxCount = 1;
int currentCount = 0;

LinkedList<Connection> connectionsPool = new LinkedList<Connection>();

public MyDataSource2() {
try {
for (int i = 0; i < initCount; i++) {
this.connectionsPool.addLast(this.createConnection());
this.currentCount++;
}
} catch (SQLException e) {
throw new ExceptionInInitializerError(e);
}
}

public Connection getConnection() throws SQLException {
synchronized (connectionsPool) {
if (this.connectionsPool.size() > 0)
return this.connectionsPool.removeFirst();

if (this.currentCount < maxCount) {
this.currentCount++;
return this.createConnection();
}

throw new SQLException("已没有链接");
}
}

public void free(Connection conn) {
this.connectionsPool.addLast(conn);
}

private Connection createConnection() throws SQLException {
Connection realConn = DriverManager.getConnection(url, user, password);
// MyConnection myConnection = new MyConnection(realConn, this);
// return myConnection;
MyConnectionHandler proxy = new MyConnectionHandler(this);
return proxy.bind(realConn);
}

public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}

public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}

public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}

public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub

}

public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub

}
}
</textarea></p>
<think>好的,用户想要自己封装一个类似MyBatis的JDBC工具,特别是实现Mapper功能,比如@Select、@Insert这样的注解,并且查询结果能返回对应的Bean实例。我需要先理清楚实现这个功能的关键步骤和可能遇到的问题。 首先,用户提到的注解功能,这需要用到Java的反射机制。可能需要定义几个自定义注解,比如@Select、@Insert、@Update、@Delete,这些注解用来标记方法对应的SQL语句。然后,通过反射读取这些注解中的SQL内容。 接下来是处理SQL执行的部分。对于查询操作,需要执行SQL并处理结果集,将结果转换为对应的Bean对象。这里涉及到结果集到对象的映射,可能需要使用反射来设置Bean的属性。需要考虑Bean的属性名和数据库字段名的对应关系,比如是否要处理下划线转驼峰命名。 然后是参数的替换。在MyBatis中,可以使用#{param}这样的占位符,用户可能希望类似的参数绑定方式。这里需要解析SQL中的占位符,替换成PreparedStatement的参数,同时处理参数类型,防止SQL注入。 另外,用户希望查询接口返回Bean类型实例,所以可能需要泛型来处理不同的返回类型。比如,定义一个Mapper接口,使用泛型指定返回的Bean类型,然后通过动态代理生成实现类,在运行时执行SQL并返回结果。 动态代理是关键部分。需要创建一个InvocationHandler,拦截接口方法的调用,读取方法上的注解,获取SQL语句,执行数据库操作,并处理结果集。这可能需要用到JDK的动态代理或者第三方库如CGLIB。 还要考虑数据库连接的管理,比如使用连接池来获取和释放连接,避免频繁创建和关闭连接带来的性能问题。可能需要引入DataSource,或者自己实现一个简单的连接池。 异常处理也是需要注意的地方。JDBC操作可能会抛出SQLException,需要妥善处理这些异常,可能转换为自定义的运行时异常,或者让用户处理受检异常。 还有事务管理的问题,但用户没有提到,可能暂时不需要处理。不过如果是简单的封装,可能不考虑事务,或者留出扩展点。 测试方面,需要确保各个注解能够正确解析,SQL执行正确,参数替换正确,结果集正确转换为Bean对象。可能需要写一些单元测试来验证。 可能的难点包括动态代理的实现、反射处理Bean属性的效率问题、复杂SQL的解析(比如结果映射中的嵌套对象),但用户的需求可能比较简单,暂时不需要处理复杂映射。 总结一下,实现步骤大概是: 1. 定义注解(Select, Insert等),用于标记方法和SQL。 2. 创建Mapper接口,定义数据库操作方法。 3. 使用动态代理生成Mapper接口的实现类,拦截方法调用。 4. 解析方法上的注解,获取SQL语句。 5. 处理参数替换,使用PreparedStatement防止注入。 6. 执行SQL,处理结果集,转换为Bean对象。 7. 管理数据库连接和资源释放。 在实现过程中,需要注意反射的性能,可以缓存一些元数据,比如Bean的字段映射信息,避免每次查询都解析。此外,处理结果集时,要确保数据库字段和Bean属性名称的对应,可能需要使用别名或者自定义映射策略。 用户可能对动态代理和反射不太熟悉,所以需要详细解释这部分。同时,可能需要提供配置数据库连接的方式,比如通过配置文件或者编程方式设置数据源。 最后,编写示例代码,展示如何使用这个封装的工具,比如定义一个UserMapper接口,使用@Select注解,并调用方法获取User对象列表。</think>以下是分步实现JDBC封装实现类MyBatis核心功能的指南: --- ### 一、核心实现步骤 #### 1. 定义注解体系 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Select { String value(); // 存储SQL语句 } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Insert { String value(); } // 类似定义Update/Delete注解 ``` #### 2. 创建Mapper代理处理器 ```java public class MapperProxy implements InvocationHandler { private DataSource dataSource; public static <T> T createProxy(Class<T> mapperInterface, DataSource dataSource) { return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[]{mapperInterface}, new MapperProxy(dataSource)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 解析方法注解 if (method.isAnnotationPresent(Select.class)) { return executeQuery(method, args); } else if (method.isAnnotationPresent(Insert.class)) { return executeUpdate(method, args); } return null; } } ``` #### 3. SQL执行结果映射 ```java private Object executeQuery(Method method, Object[] args) { Select select = method.getAnnotation(Select.class); String sql = select.value(); try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { // 参数绑定 for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } ResultSet rs = ps.executeQuery(); return resultSetToBean(rs, method.getReturnType()); } catch (SQLException e) { throw new RuntimeException(e); } } private <T> T resultSetToBean(ResultSet rs, Class<T> type) throws SQLException { if (!rs.next()) return null; T instance = type.newInstance(); ResultSetMetaData metaData = rs.getMetaData(); for (int i = 1; i <= metaData.getColumnCount(); i++) { String colName = metaData.getColumnLabel(i); Object value = rs.getObject(i); // 字段名转换(处理下划线命名) String fieldName = toCamelCase(colName); // 反射设置属性值 Field field = type.getDeclaredField(fieldName); field.setAccessible(true); field.set(instance, value); } return instance; } ``` #### 4. 参数处理(支持#{param}语法) ```java private String parseSQL(String originSQL, Object[] args) { // 使用正则表达式匹配占位符 Pattern pattern = Pattern.compile("#\\{([^}]+)\\}"); Matcher matcher = pattern.matcher(originSQL); StringBuffer parsedSQL = new StringBuffer(); int paramIndex = 0; while (matcher.find()) { matcher.appendReplacement(parsedSQL, "?"); // 可以在此处实现参数名参数的映射(需要方法参数名称保留) paramIndex++; } matcher.appendTail(parsedSQL); return parsedSQL.toString(); } ``` --- ### 二、使用示例 #### 1. 定义实体类 ```java public class User { private Long id; private String userName; private Integer age; // getters/setters } ``` #### 2. 声明Mapper接口 ```java public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User getUserById(Long id); @Insert("INSERT INTO users(user_name, age) VALUES(#{userName}, #{age})") int insertUser(User user); } ``` #### 3. 使用入口 ```java public class Main { public static void main(String[] args) { // 配置数据源(需自行实现) DataSource dataSource = createDataSource(); UserMapper userMapper = MapperProxy.createProxy( UserMapper.class, dataSource); // 执行查询 User user = userMapper.getUserById(1L); System.out.println(user.getUserName()); } } ``` --- ### 三、扩展优化方向 1. **类型处理器**:添加TypeHandler处理特殊类型转换(如Date类型) 2. **连接池集成**:整合Druid/HikariCP等连接池 3. **事务管理**:添加@Transactional注解支持 4. **XML配置支持**:实现SQL语句的外部化配置 5. **缓存机制**:添加一级/二级查询缓存 6. **批量操作**:实现批量插入/更新支持 --- ### 四、注意事项 1. 方法参数名保留问题:编译时需添加-parameters参数 2. 性能优化:缓存PreparedStatement、反射元数据等 3. 安全防护:严格防范SQL注入漏洞 4. 异常处理:自定义异常体系替代SQLException 5. 嵌套结果映射:处理复杂对象关联关系(需要递归映射) 通过上述实现即可构建一个轻量级的ORM框架核心,实际生产环境建议直接使用成熟的MyBatis框架,本实现主要用于理解底层原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值