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源代码下载