JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
连接数据库
使用的连接类是java.sql包中的DriverManager类,方法名:getConnection(),会返回一个数据库Connection对象,它的代码如下:
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
getConnect重载了内部私有的方法,代码有点长:
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
查询数据库
有了连接对象,接着就可以实现查询了,查询也很简单,需要创建一个Statement对象,然后通过Statement对象去执行SQL语句。执行成功之后会返回一个ResultSet的对象,当然这只是针对SELECT语句,如果是UPDATE、INSERT、DELETE等这些语句就只会返回一个int,等于0则失败,大于0则成功。
//这种方法可能比较直接点,后期补容易管理。 //SQL语句一般都是像这样子:SELETE * FROM user where username = 'test' Statement statement = conn.createStatement(); ResultSet res = statement.executeQuery(sql); //也可以使用JDBC语句 //JDBC的SQL语句:SELETE * FROM user where username = ? //问号“?”就是下面setString所对应的,位置是从1开始。 //所以SQL语句最终会转换成:SELETE * FROM user where username = 'test' PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1,"test"); ps.execute(); ps.executeQuery(); ps.executeUpdate(); ResultSet res = ps.getResultSet();
处理数据
返回的始终都是一个ResultSet对象,接着就是把数据给处理出来,ResultSet对象需要执行next()方法,将指针移到下一行的位置。
Statement statement = conn.createStatement();
ResultSet res = statement.executeQuery(sql);
User user = null;
if (res.next()) {
user = new User();
user.setId(res.getInt(1));
user.setUsername(res.getString(2));
user.setLoginPassword(res.getString(3));
user.setNickname(res.getString(4));
}
res.close();
statement.close();
处理完之后,别忘记了关闭,这只是一个简单的示例,具体实现看下面的。
实例代码:
IDatabaseService.java接口
public interface IDatabaseService<T> {
void connect() throws SQLException;
T getRow(String sql) throws SQLException;
Object getOne(String sql) throws SQLException;
List<T> getAll(String sql) throws SQLException;
int insert(T record) throws SQLException;
int update(T record) throws SQLException;
int delete(String sql) throws SQLException;
int exec(String sql,String action) throws SQLException;
void close() throws SQLException;
}
注:该有的方法都有了,以为我们可能处理的不止一个User对象类,所以使用了泛型。
DBService.java对象类(抽象)
public abstract class DBService<T> extends Console implements IDatabaseService<T> {
protected static final String TAG = "DBService:";
protected static final String ACTION_INSERT = "INSERT";
protected static final String ACTION_QUERY = "QUERY";
protected static final String ACTION_UPDATE = "UPDATE";
protected static final String ACTION_DELETE = "DELETE";
private String driver;
private String url;
private String username;
private String userpwd;
private Connection conn;
private Statement statement;
public DBService() {
Properties properties = new Properties();
InputStream in = null;
in = ClassLoaderRepository.class.getResourceAsStream("/db.properties");
try {
properties.load(in);
setDriver(properties.get("driver").toString());
setUrl(properties.get("url").toString());
setUsername(properties.get("username").toString());
setUserpwd(properties.get("userpwd").toString());
} catch (IOException e) {
e.printStackTrace();
}
}
public DBService(String url, String username, String userpwd) {
this.url = url;
this.username = username;
this.userpwd = userpwd;
}
@Override
public void connect() throws SQLException {
if (conn == null) {
conn = DriverManager.getConnection(url, username, userpwd);
conn.setAutoCommit(false);
}
statement = conn.createStatement();
}
@Override
public T getRow(String sql) throws SQLException {
int r = exec(sql, ACTION_QUERY);
T t = null;
if (r > 0) {
ResultSet res = statement.getResultSet();
res.next();
t = parserClass(res);
res.close();
}
statement.close();
return t;
}
@Override
public Object getOne(String sql) throws SQLException {
exec(sql, ACTION_QUERY);
ResultSet res = statement.getResultSet();
if (res.next()){
Object o = res.getString(1);
res.close();
statement.close();
return o;
}
return null;
}
@Override
public List<T> getAll(String sql) throws SQLException {
List<T> list = new ArrayList<>();
int r = exec(sql, ACTION_QUERY);
ResultSet res = statement.getResultSet();
while (res.next()) {
list.add(parserClass(res));
}
res.close();
statement.close();
return list;
}
@Override
public int insert(T record) throws SQLException {
String sql = this.parserClass(record, ACTION_INSERT);
return exec(sql, ACTION_INSERT);
}
@Override
public int update(T record) throws SQLException {
String sql = this.parserClass(record, ACTION_UPDATE);
return exec(sql, ACTION_UPDATE);
}
@Override
public int delete(String sql) throws SQLException {
return exec(sql, ACTION_DELETE);
}
@Override
public int exec(String sql, String action) throws SQLException {
if (sql == null) {
throw new SQLException("SQL string must be require.");
}
this.connect();
int i = 0;
info(TAG, sql);
try {
if (action.equals(ACTION_QUERY)) {
ResultSet set = statement.executeQuery(sql);
i = 1;
} else {
i = statement.executeUpdate(sql);
}
} catch (SQLException e) {
e.printStackTrace();
if (!action.equals(ACTION_QUERY)) {
conn.rollback();
}
} finally {
if (!action.equals(ACTION_QUERY)) {
conn.commit();
}
}
return i;
}
@Override
public void close() throws SQLException {
if (conn != null) {
conn.close();
}
}
public abstract T parserClass(ResultSet res) throws SQLException;
public abstract String parserClass(T res, String action);
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpwd() {
return userpwd;
}
public void setUserpwd(String userpwd) {
this.userpwd = userpwd;
}
public Connection getConn() {
return conn;
}
public void setConn(Connection conn) {
this.conn = conn;
}
}
注:按照命名规则都很容易理解大致是什么意思,主要是两个抽象的方法:
- public abstract T parserClass(ResultSet res) throws SQLException;
- public abstract String parserClass(T res, String action);
第一个是为了执行SELECT返回的数据加工处理,这里交给子类去处理,因为我们不可能只有一个数据表,那么把通用的事件交给父类去执行,特殊的事件交给子类,是一个很好的开发思维。
第二个是对INSERT、UPDATE执行前的数据加工,返回的是一条SQL语句,个人比较懒,所以直接使用了SQL语句做例子。
UserMapper.java对象类
public class UserMapper extends DBService<User> {
@Override
public User parserClass(ResultSet res) throws SQLException {
if (res.getRow()==0){
return null;
}
try {
ResultSetMetaData metaData = res.getMetaData();
Class classz = Class.forName(User.class.getName());
User user = (User) classz.newInstance();
Field[] fields = classz.getDeclaredFields();
List<String> keys = new ArrayList<>();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
keys.add(metaData.getColumnName(i));
}
for (Field f : fields) {
if (keys.contains(f.getName())) {
f.setAccessible(true);
if ((f.getType().equals(String.class))) {
f.set(user, res.getString(f.getName()));
} else if ((f.getType().equals(Integer.class))) {
f.set(user, res.getInt(f.getName()));
}
}
}
return user;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
@Override
public String parserClass(User res, String action) {
StringBuffer sql = new StringBuffer();
Map<String, Object> data = new HashMap<>();
try {
Class classz = Class.forName(res.getClass().getName());
Field[] fields = classz.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
Object obj = f.get(res);
if (obj != null) {
data.put(f.getName(), obj);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Set<String> keys = data.keySet();
if (action.equals(ACTION_INSERT)) {
sql.append("INSERT INTO `user` (`id`");
String v = "null";
for (String k : keys) {
if ("id".equals(k)) {
continue;
}
sql.append(",")
.append("`")
.append(k)
.append("`");
v += ",'" + data.get(k).toString() + "'";
}
sql.append(") VALUES");
sql.append("(")
.append(v)
.append(");");
} else {
sql.append("UPDATE `user` SET id=").append(res.getId());
for (String k : keys) {
if ("id".equals(k)) {
continue;
}
sql.append(",").append(k)
.append("=")
.append("'")
.append(data.get(k).toString())
.append("'");
}
sql.append(" WHERE id=")
.append(res.getId());
}
return sql.toString();
}
}
注:在public User parserClass(ResultSet res)方法中,我使用的是反射,这样子比较简单一点,通过ResultSetMetaData对象可以获取数据表中的列表,通过列名跟User对象中的私有字段对比,就可以把数据set进去。
public String parserClass(User res, String action)方法中,我也使用了反射,将User对象中不等于null的私有字段组装成SQL语句,这里其实是用缺陷的,比如:如果你在User对象中新增一个ids的字段,但是数据库表中没有这个字段名,同时在更新数据的时候,ids的值不为null,那么这里的代码就会抛出异常了。
想要完美一点,可以借助注解,比如在字段中添加一个@Field的注解,只要有这个注解的就属于数据库的字段名,当然,也可以改进一下方法,比如传入一个Set对象,把字段名全部传入进来,这可能是最简单的方式。
JDBC的操作基本上就这些,结合以上的代码,可以写出一个登陆、注册、修改密码及获取用户列表的功能,以下是截图,最后是代码打包地址:
源码包:
运行截图:
源码下载:java_jdbc源代码下载




