JDBC基础
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,使用这个类库可以以一种标准的方法、方便地访问数据库资源,JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
Java.sql.Driver 接口是所有 JDBC驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。在程序中不需要直接去访问实现了 Driver接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。
加载 JDBC驱动需调用 Class类的静态方法 forName(),向其传递要加载的 JDBC驱动的类名。DriverManager类是驱动程序管理器类,负责管理驱动程序,通常不用显式调用 DriverManager类的registerDriver()方法来注册驱动程序类的实例,因为 Driver接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver()方法来注册自身的一个实例。
可以调用 DriverManager类的 getConnection()方法建立到数据库的连接。JDBC URL用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL选择正确的驱动程序,从而建立到数据库的连接。
JDBC URL的标准由三部分组成,各部分间用冒号分隔。
jdbc:<子协议>:<子名称>
协议:JDBC URL中的协议总是jdbc
子协议:子协议用于标识一个数据库驱动程序
子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息
测试代码:
/**
* Driver 是一个接口:数据库厂商必须提供实现的接口.能从其中获取数据库连接.
* 可以通过 Driver的实现类对象获取数据库连接.
*
* 1. 加入 mysql驱动
* 1). 解压mysql-connector-java-5.1.7.zip
* 2). 在当前项目下新建 lib目录
* 3). 把mysql-connector-java-5.1.7-bin.jar复制到 lib 目录下
* 4). 右键 build-path ,add to buildpath加入到类路径下.s
* @throws SQLException
*/
@Test
publicvoid testDriver()throws SQLException {
//1.创建一个 Driver实现类的对象
Driver driver = newcom.mysql.jdbc.Driver();
//2.准备连接数据库的基本信息: url, user, password
String url = "jdbc:mysql://localhost:3306/test";
Properties info = new Properties();
info.put("user","root");
info.put("password","1230");
//3.调用 Driver 接口的 connect(url,info)获取数据库连接
Connection connection =driver.connect(url, info);
System.out.println(connection);
}
/**
* 编写一个通用的方法,在不修改源程序的情况下,可以获取任何数据库的连接
* 解决方案:把数据库驱动 Driver实现类的全类名、url、user、password放入一个
* 配置文件中,通过修改配置文件的方式实现和具体的数据库解耦.
* @throws Exception
*/
public ConnectiongetConnection()throws Exception{
String driverClass = null;
String jdbcUrl = null;
String user = null;
String password = null;
//读取类路径下的 jdbc.properties文件
InputStream in =
getClass().getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(in);
driverClass = properties.getProperty("driver");
jdbcUrl = properties.getProperty("jdbcUrl");
user = properties.getProperty("user");
password = properties.getProperty("password");
//通过反射常见 Driver对象.
Driver driver =
(Driver)Class.forName(driverClass).newInstance();
Properties info = new Properties();
info.put("user", user);
info.put("password", password);
//通过 Driver的 connect 方法获取数据库连接.
Connection connection =driver.connect(jdbcUrl, info);
return connection;
}
/**
* DriverManager 是驱动的管理类.
* 1). 可以通过重载的 getConnection()方法获取数据库连接.较为方便
* 2). 可以同时管理多个驱动程序:若注册了多个数据库连接,则调用 getConnection()
* 方法时传入的参数不同,即返回不同的数据库连接。
* @throws Exception
*/
@Test
publicvoidtestDriverManager()throws Exception{
//1.准备连接数据库的 4 个字符串.
//驱动的全类名.
String driverClass = "com.mysql.jdbc.Driver";
//JDBC URL
String jdbcUrl = "jdbc:mysql:///test";
//user
String user = "root";
//password
String password = "1230";
//2.加载数据库驱动程序(对应的 Driver实现类中有注册驱动的静态代码块.)
Class.forName(driverClass);
//3.通过 DriverManager的 getConnection()方法获取数据库连接.
Connection connection =
DriverManager.getConnection(jdbcUrl, user, password);
System.out.println(connection);
}
public ConnectiongetConnection2()throws Exception{
//1.准备连接数据库的 4 个字符串.
//1).创建 Properties对象
Properties properties = new Properties();
//2).获取 jdbc.properties对应的输入流
InputStream in =
this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
//3).加载 2)对应的输入流
properties.load(in);
//4).具体决定 user, password等4 个字符串.
String user = properties.getProperty("user");
String password =properties.getProperty("password");
String jdbcUrl = properties.getProperty("url");
String driver = properties.getProperty("driverClass");
//2.加载数据库驱动程序(对应的 Driver实现类中有注册驱动的静态代码块.)
Class.forName(driver);
//3.通过 DriverManager的 getConnection()方法获取数据库连接.
returnDriverManager.getConnection(jdbcUrl, user, password);
}
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/adb
user=root
password=123
访问数据库
数据库连接被用于向数据库服务器发送命令和 SQL语句,在连接建立后,需要对数据库进行访问,执行 sql语句。在 java.sql包中有 3 个接口分别定义了对数据库的调用的不同方式:
- Statement
- PrepatedStatement
- CallableStatement
通过调用 Connection对象的 createStatement方法创建该对象
该对象用于执行静态的 SQL语句,并且返回执行结果。Statement接口中定义了下列方法用于执行 SQL语句:
ResultSet excuteQuery(String sql)
int excuteUpdate(String sql)
通过调用 Statement对象的 excuteQuery()方法创建该对象。ResultSet对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet接口由数据库厂商实现。ResultSet对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet对象的 next() 方法移动到下一行。
ResultSet 接口的常用方法:
boolean next()
getString()
/**
* 通过 JDBC向指定的数据表中插入一条记录.
*
* 1. Statement: 用于执行 SQL语句的对象
* 1). 通过 Connection的 createStatement()方法来获取
* 2). 通过 executeUpdate(sql)可以执行 SQL 语句.
* 3). 传入的 SQL可以是 INSRET, UPDATE或 DELETE. 但不能是 SELECT
*
* 2. Connection、Statement都是应用程序和数据库服务器的连接资源.使用后一定要关闭.
* 需要在 finally中关闭 Connection和 Statement对象.
*
* 3. 关闭的顺序是:先关闭后获取的. 即先关闭 Statement 后关闭 Connection
*/
@Test
publicvoid testStatement()throws Exception{
//1.获取数据库连接
Connection conn = null;
Statement statement = null;
try {
conn = getConnection2();
//3.准备插入的 SQL 语句
String sql = null;
// sql = "INSERT INTO customers (NAME, EMAIL, BIRTH) " +
// "VALUES('XYZ','xyz@atguigu.com', '1990-12-12')";
// sql = "DELETE FROM customers WHERE id = 1";
sql = "UPDATEcustomers SET name = 'TOM' " +
"WHERE id = 4";
System.out.println(sql);
//4.执行插入.
//1).获取操作 SQL 语句的 Statement 对象:
//调用 Connection的 createStatement()方法来获取
statement = conn.createStatement();
//2).调用 Statement对象的 executeUpdate(sql)执行 SQL 语句进行插入
statement.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally{
if (statement !=null) {
try {
statement.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if (conn !=null) {
try {
conn.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/**
* ResultSet: 结果集.封装了使用 JDBC 进行查询的结果.
* 1. 调用 Statement对象的 executeQuery(sql)可以得到结果集.
* 2. ResultSet 返回的实际上就是一张数据表.有一个指针指向数据表的第一行的前面.
* 可以调用 next()方法检测下一行是否有效.若有效该方法返回 true,且指针下移. 相当于
* Iterator 对象的 hasNext()和 next() 方法的结合体
* 3. 当指针对位到一行时,可以通过调用 getXxx(index)或 getXxx(columnName)
* 获取每一列的值.例如: getInt(1), getString("name")
* 4. ResultSet 当然也需要进行关闭.
*/
@Test
publicvoid testResultSet(){
//获取 id=4的 customers数据表的记录, 并打印
Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try {
//1.获取 Connection
conn = JDBCTools.getConnection();
System.out.println(conn);
//2.获取 Statement
statement = conn.createStatement();
System.out.println(statement);
//3.准备 SQL
String sql = "SELECTid, name, email, birth " +
"FROM customers";
//4.执行查询, 得到 ResultSet
rs = statement.executeQuery(sql);
System.out.println(rs);
//5.处理 ResultSet
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString("name");
String email = rs.getString(3);
Date birth = rs.getDate(4);
System.out.println(id);
System.out.println(name);
System.out.println(email);
System.out.println(birth);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
//6.关闭数据库资源.
if(rs !=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement !=null) {
try {
statement.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if (conn !=null) {
try {
conn.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
- java.sql.DriverManager用来装载驱动程序,获取数据库连接。
- java.sql.Connection完成对某一指定数据库的联接
- java.sql.Statement在一个给定的连接中作为SQL执行声明的容器,他包含了两个重要的子类型。
- Java.sql.PreparedSatement 用于执行预编译的sql声明
- Java.sql.CallableStatement用于执行数据库中存储过程的调用
- java.sql.ResultSet对于给定声明取得结果的途径
可以通过调用 Connection对象的 preparedStatement(String sql)方法获取PreparedStatement对象。PreparedStatement接口是 Statement的子接口,它表示一条预编译过的 SQL语句。PreparedStatement对象所代表的 SQL 语句中的参数用问号(?)来表示,调用PreparedStatement对象的setXXX(index,value)方法来设置这些参数。setXXX()方法有两个参数,第一个参数是要设置的 SQL语句中的参数的索引(从 1开始),第二个是设置的 SQL语句中的参数的值。
publicvoid testPreparedStatement() {
Connection connection = null;
PreparedStatement preparedStatement =null;
try {
connection = JDBCTools.getConnection();
String sql = "INSERTINTO customers (name, email, birth) "
+ "VALUES(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"ATGUIGU");
preparedStatement.setString(2,"simpleit@163.com");
preparedStatement.setDate(3,
new Date(new java.util.Date().getTime()));
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (statement !=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection !=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
publicvoid addNewStudent2(Student student) {
String sql = "INSERTINTO examstudent(flowid, type, idcard, "
+ "examcard, studentname, location,grade) "
+ "VALUES(?,?,?,?,?,?,?)";
JDBCTools.update(sql, student.getFlowId(), student.getType(),
student.getIdCard(),student.getExamCard(),
student.getStudentName(),student.getLocation(),
student.getGrade());
}
/**
* 执行 SQL语句, 使用 PreparedStatement
* @param sql
* @param args:填写 SQL 占位符的可变参数
*/
publicstaticvoid update(String sql, Object ... args){
Connection connection = null;
PreparedStatement preparedStatement =null;
try {
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
for(int i =0; i < args.length; i++){
preparedStatement.setObject(i +1, args[i]);
}
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(null,preparedStatement, connection);
}
}
PreparedStatement vs Statement
PreparedStatement 能最大可能提高性能:
- DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
- 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.
- (语法检查,语义检查,翻译成二进制命令,缓存)
PreparedStatement 可以防止 SQL注入
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL语句段或命令,从而利用系统的 SQL引擎完成恶意行为的做法。对于 Java而言,要防范 SQL 注入,只要用PreparedStatement取代 Statement就可以了
/**
* 使用 PreparedStatement将有效的解决 SQL 注入问题.
*/
@Test
publicvoid testSQLInjection2() {
String username = "a' OR PASSWORD = ";
String password = " OR '1'='1";
String sql = "SELECT* FROM users WHERE username = ? "
+ "AND password = ?";
Connection connection = null;
PreparedStatement preparedStatement =null;
ResultSet resultSet = null;
try {
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);//从1开始往后
preparedStatement.setString(2, password);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("登录成功!");
} else {
System.out.println("用户名和密码不匹配或用户名不存在. ");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
}
/**
* SQL 注入.
*/
@Test
publicvoid testSQLInjection() {
String username = "a' OR PASSWORD = ";
String password = " OR '1'='1";
String sql = "SELECT* FROM users WHERE username = '" + username
+ "' AND " +"password = '" + password +"'";
System.out.println(sql);
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCTools.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
System.out.println("登录成功!");
} else {
System.out.println("用户名和密码不匹配或用户名不存在. ");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(resultSet, statement, connection);
}
}
可用于获取关于 ResultSet对象中列的类型和属性信息的对象:
- getColumnName(int column):获取指定列的名称
- getColumnLable(int column):获取指定列的别名(从1开始)
- getColumnCount():返回当前 ResultSet 对象中的列数。
- getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
- getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
- isNullable(int column):指示指定列中的值是否可以为 null。
- isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
/**
* ResultSetMetaData: 描述结果集的元数据.
* 可以得到结果集中的基本信息:结果集中有哪些列, 列名, 列的别名等.
* 结合反射可以写出通用的查询方法.
*/
@Test
publicvoid testResultSetMetaData(){
Connection connection = null;
PreparedStatement preparedStatement =null;
ResultSet resultSet = null;
try {
connection = JDBCTools.getConnection();
String sql = "SELECTid, name customerName, email, birth " +
"FROM customers";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
//1.得到 ResultSetMetaData对象
ResultSetMetaData rsmd = resultSet.getMetaData();
//2.得到列的个数
int columnCount = rsmd.getColumnCount();
System.out.println(columnCount);
for(int i =0 ; i < columnCount; i++){
//3.得到列名
String columnName =rsmd.getColumnName(i +1);
//4.得到列的别名
String columnLabel =rsmd.getColumnLabel(i +1);
System.out.println(columnName +", " + columnLabel);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
}
/**
* 通用的查询方法:可以根据传入的 SQL、Class对象返回 SQL 对应的记录的对象
* @param clazz:描述对象的类型
* @param sql: SQL语句。可能带占位符
* @param args:填充占位符的可变参数。
* @return
*/
public <T> T get(Class<T> clazz,String sql, Object... args) {
T entity = null;
Connection connection = null;
PreparedStatement preparedStatement =null;
ResultSet resultSet = null;
try {
//1.得到 ResultSet对象
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i =0; i < args.length; i++) {
preparedStatement.setObject(i +1, args[i]);
}
resultSet = preparedStatement.executeQuery();
//2.得到 ResultSetMetaData对象
ResultSetMetaData rsmd = resultSet.getMetaData();
//3.创建一个 Map<String, Object>对象, 键: SQL 查询的列的别名,
//值:列的值
Map<String, Object> values =new HashMap<>();
//4.处理结果集. 利用 ResultSetMetaData填充 3 对应的 Map 对象
if(resultSet.next()){
for(int i =0; i < rsmd.getColumnCount(); i++){
String columnLabel = rsmd.getColumnLabel(i +1);
Object columnValue =resultSet.getObject(i +1);
values.put(columnLabel,columnValue);
}
}
//5.若 Map 不为空集, 利用反射创建 clazz对应的对象
if(values.size() >0){
entity = clazz.newInstance();
//6.遍历 Map 对象, 利用反射为 Class对象的对应的属性赋值.
for(Map.Entry<String, Object> entry:values.entrySet()){
String fieldName = entry.getKey();
Object value =entry.getValue();
ReflectionUtils.setFieldValue(entity, fieldName, value);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
return entity;
}
import java.lang.reflect.Field;
importjava.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* 反射的 Utils函数集合
* 提供访问私有变量,获取泛型类型 Class,提取集合中元素属性等Utils函数
* @author Administrator
*
*/
publicclassReflectionUtils {
/**
* 通过反射,获得定义 Class 时声明的父类的泛型参数的类型
* 如: public EmployeeDao extends BaseDao<Employee,String>
* @param clazz
* @param index
* @return
*/
@SuppressWarnings("unchecked")
publicstatic Class getSuperClassGenricType(Class clazz,int index){
Type genType = clazz.getGenericSuperclass();
if(!(genTypeinstanceof ParameterizedType)){
return Object.class;
}
Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
if(index >= params.length || index <0){
return Object.class;
}
if(!(params[index]instanceof Class)){
return Object.class;
}
return (Class) params[index];
}
/**
* 通过反射,获得 Class 定义中声明的父类的泛型参数类型
* 如: public EmployeeDao extends BaseDao<Employee,String>
* @param <T>
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
publicstatic<T> Class<T> getSuperGenericType(Class clazz){
return getSuperClassGenricType(clazz,0);
}
/**
* 循环向上转型,获取对象的 DeclaredMethod
* @param object
* @param methodName
* @param parameterTypes
* @return
*/
publicstatic Method getDeclaredMethod(Object object, String methodName,Class<?>[] parameterTypes){
for(Class<?> superClass =object.getClass(); superClass != Object.class; superClass =superClass.getSuperclass()){
try {
//superClass.getMethod(methodName,parameterTypes);
return superClass.getDeclaredMethod(methodName,parameterTypes);
} catch (NoSuchMethodException e) {
//Method不在当前类定义, 继续向上转型
}
//..
}
returnnull;
}
/**
* 使 filed变为可访问
* @param field
*/
publicstaticvoid makeAccessible(Field field){
if(!Modifier.isPublic(field.getModifiers())){
field.setAccessible(true);
}
}
/**
* 循环向上转型,获取对象的 DeclaredField
* @param object
* @param filedName
* @return
*/
publicstatic Field getDeclaredField(Object object, String filedName){
for(Class<?> superClass =object.getClass(); superClass != Object.class; superClass =superClass.getSuperclass()){
try {
return superClass.getDeclaredField(filedName);
} catch (NoSuchFieldException e) {
//Field不在当前类定义, 继续向上转型
}
}
returnnull;
}
/**
* 直接调用对象方法,而忽略修饰符(private, protected)
* @param object
* @param methodName
* @param parameterTypes
* @param parameters
* @return
* @throws InvocationTargetException
* @throws IllegalArgumentException
*/
publicstatic Object invokeMethod(Object object, String methodName, Class<?> []parameterTypes,
Object [] parameters) throwsInvocationTargetException{
Method method = getDeclaredMethod(object, methodName, parameterTypes);
if(method ==null){
thrownew IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object +"]");
}
method.setAccessible(true);
try {
return method.invoke(object, parameters);
} catch(IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
returnnull;
}
/**
* 直接设置对象属性值,忽略 private/protected修饰符, 也不经过 setter
* @param object
* @param fieldName
* @param value
*/
publicstaticvoid setFieldValue(Object object, StringfieldName, Object value){
Field field = getDeclaredField(object, fieldName);
if (field ==null)
thrownew IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object +"]");
makeAccessible(field);
try {
field.set(object, value);
} catch (IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
}
/**
* 直接读取对象的属性值,忽略 private/protected修饰符, 也不经过 getter
* @param object
* @param fieldName
* @return
*/
publicstatic Object getFieldValue(Object object, String fieldName){
Field field = getDeclaredField(object, fieldName);
if (field ==null)
thrownew IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object +"]");
makeAccessible(field);
Object result = null;
try {
result = field.get(object);
} catch (IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
return result;
}
}
publicclassDAO {
// INSERT, UPDATE, DELETE操作都可以包含在其中
publicvoid update(String sql, Object... args) {
Connection connection = null;
PreparedStatement preparedStatement =null;
try {
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i =0; i < args.length; i++) {
preparedStatement.setObject(i +1, args[i]);
}
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(null, preparedStatement,connection);
}
}
//查询一条记录, 返回对应的对象
public <T> T get(Class<T> clazz,String sql, Object... args) {
List<T> result = getForList(clazz, sql, args);
if(result.size() >0){
return result.get(0);
}
returnnull;
}
/**
* 传入 SQL语句和 Class 对象, 返回 SQL语句查询到的记录对应的 Class类的对象的集合
* @param clazz:对象的类型
* @param sql: SQL语句
* @param args:填充 SQL 语句的占位符的可变参数.
* @return
*/
public <T> List<T>getForList(Class<T> clazz,
String sql, Object... args) {
List<T> list = newArrayList<>();
Connection connection = null;
PreparedStatement preparedStatement =null;
ResultSet resultSet = null;
try {
//1.得到结果集
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i =0; i < args.length; i++) {
preparedStatement.setObject(i +1, args[i]);
}
resultSet = preparedStatement.executeQuery();
//2.处理结果集, 得到 Map 的 List,其中一个 Map 对象
//就是一条记录. Map的 key 为 reusltSet 中列的别名, Map 的 value
//为列的值.
List<Map<String, Object>> values =
handleResultSetToMapList(resultSet);
//3.把 Map 的 List 转为 clazz对应的 List
//其中 Map的 key 即为 clazz 对应的对象的 propertyName,
//而 Map的 value 即为 clazz 对应的对象的 propertyValue
list = transfterMapListToBeanList(clazz, values);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
return list;
}
public <T> List<T>transfterMapListToBeanList(Class<T> clazz,
List<Map<String, Object>> values)throws InstantiationException,
IllegalAccessException, InvocationTargetException {
List<T> result = newArrayList<>();
T bean = null;
if (values.size() >0) {
for (Map<String, Object> m : values){
bean = clazz.newInstance();
for (Map.Entry<String, Object> entry :m.entrySet()) {
String propertyName = entry.getKey();
Object value =entry.getValue();
BeanUtils.setProperty(bean,propertyName, value);
}
// 13.把 Object 对象放入到 list 中.
result.add(bean);
}
}
return result;
}
/**
* 处理结果集,得到 Map 的一个 List, 其中一个 Map对象对应一条记录
*
* @param resultSet
* @return
* @throws SQLException
*/
public List<Map<String, Object>>handleResultSetToMapList(
ResultSet resultSet) throws SQLException {
// 5.准备一个 List<Map<String, Object>>:
//键: 存放列的别名,值: 存放列的值.其中一个 Map 对象对应着一条记录
List<Map<String, Object>> values =new ArrayList<>();
List<String> columnLabels = getColumnLabels(resultSet);
Map<String, Object> map = null;
// 7.处理 ResultSet,使用 while 循环
while (resultSet.next()) {
map = new HashMap<>();
for (String columnLabel : columnLabels) {
Object value =resultSet.getObject(columnLabel);
map.put(columnLabel, value);
}
// 11.把一条记录的一个 Map对象放入 5 准备的 List 中
values.add(map);
}
return values;
}
/**
* 获取结果集的 ColumnLabel对应的 List
*
* @param rs
* @return
* @throws SQLException
*/
private List<String>getColumnLabels(ResultSet rs)throws SQLException {
List<String> labels = new ArrayList<>();
ResultSetMetaData rsmd = rs.getMetaData();
for (int i =0; i < rsmd.getColumnCount(); i++) {
labels.add(rsmd.getColumnLabel(i +1));
}
return labels;
}
//返回某条记录的某一个字段的值或一个统计的值(一共有多少条记录等.)
public <E> E getForValue(String sql,Object... args) {
//1.得到结果集: 该结果集应该只有一行, 且只有一列
Connection connection = null;
PreparedStatement preparedStatement =null;
ResultSet resultSet = null;
try {
//1.得到结果集
connection = JDBCTools.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i =0; i < args.length; i++) {
preparedStatement.setObject(i +1, args[i]);
}
resultSet =preparedStatement.executeQuery();
if(resultSet.next()){
return (E) resultSet.getObject(1);
}
} catch(Exception ex){
ex.printStackTrace();
} finally{
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
//2.取得结果
returnnull;
}
}
Java 通过JDBC获得连接以后,得到一个Connection对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信息。根据这些信息,JDBC可以访问一个实现事先并不了解的数据库。获取这些信息的方法都是在DatabaseMetaData类的对象上实现的,而DataBaseMetaData对象是在Connection对象上获得的。 DatabaseMetaData 类中提供了许多方法用于获得数据源的各种信息,通过这些方法可以非常详细的了解数据库的信息:
- getURL():返回一个String类对象,代表数据库的URL。
- getUserName():返回连接当前数据库管理系统的用户名。
- isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
- getDatabaseProductName():返回数据库的产品名称。
- getDatabaseProductVersion():返回数据库的版本号。
- getDriverName():返回驱动驱动程序的名称。
- getDriverVersion():返回驱动程序的版本号。
/**
* DatabaseMetaData 是描述数据库的元数据对象.
* 可以由 Connection得到.
* 了解.
*/
@Test
publicvoid testDatabaseMetaData(){
Connection connection = null;
ResultSet resultSet = null;
try {
connection = JDBCTools.getConnection();
DatabaseMetaData data = connection.getMetaData();
//可以得到数据库本身的一些基本信息
//1.得到数据库的版本号
int version =data.getDatabaseMajorVersion();
System.out.println(version);
//2.得到连接到数据库的用户名
String user = data.getUserName();
System.out.println(user);
//3.得到 MySQL 中有哪些数据库
resultSet = data.getCatalogs();
while(resultSet.next()){
System.out.println(resultSet.getString(1));
}
//...
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(resultSet,null, connection);
}
}
/**
* 取得数据库自动生成的主键
*/
@Test
publicvoid testGetKeyValue() {
Connection connection = null;
PreparedStatement preparedStatement =null;
try {
connection = JDBCTools.getConnection();
String sql = "INSERTINTO customers(name, email, birth)" +
"VALUES(?,?,?)";
// preparedStatement = connection.prepareStatement(sql);
//使用重载的 prepareStatement(sql, flag)
//来生成 PreparedStatement对象
preparedStatement = connection.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1,"ABCDE");
preparedStatement.setString(2,"abcde@atguigu.com");
preparedStatement.setDate(3,
new Date(new java.util.Date().getTime()));
preparedStatement.executeUpdate();
//通过 getGeneratedKeys()获取包含了新生成的主键的 ResultSet对象
//在 ResultSet中只有一列 GENERATED_KEY,用于存放新生成的主键值.
ResultSet rs = preparedStatement.getGeneratedKeys();
if(rs.next()){
System.out.println(rs.getObject(1));
}
ResultSetMetaData rsmd = rs.getMetaData();
for(int i =0; i < rsmd.getColumnCount(); i++){
System.out.println(rsmd.getColumnName(i +1));
}
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(null,preparedStatement, connection);
}
}
LOB,即Large Objects(大对象),是用来存储大量的二进制和文本数据的一种数据类型(一个LOB字段可存储可多达4GB的数据)
/**
* 读取 blob数据:
* 1. 使用 getBlob方法读取到 Blob 对象
* 2. 调用 Blob的 getBinaryStream()方法得到输入流。再使用 IO操作即可.
*/
@Test
publicvoid readBlob(){
Connection connection = null;
PreparedStatement preparedStatement =null;
ResultSet resultSet = null;
try {
connection = JDBCTools.getConnection();
String sql = "SELECTid, name customerName, email, birth, picture "
+ "FROM customers WHERE id = 13";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
int id = resultSet.getInt(1);
String name =resultSet.getString(2);
String email =resultSet.getString(3);
System.out.println(id +", " + name +", " + email);
Blob picture =resultSet.getBlob(5);
InputStream in =picture.getBinaryStream();
System.out.println(in.available());
OutputStream out = new FileOutputStream("flower.jpg");
byte [] buffer =newbyte[1024];
int len =0;
while((len = in.read(buffer)) != -1){
out.write(buffer,0, len);
}
in.close();
out.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(resultSet, preparedStatement, connection);
}
}
/**
* 插入 BLOB类型的数据必须使用 PreparedStatement:因为 BLOB类型
* 的数据时无法使用字符串拼写的。
*
* 调用 setBlob(int index, InputStream inputStream)
*/
@Test
publicvoid testInsertBlob(){
Connection connection = null;
PreparedStatement preparedStatement =null;
try {
connection = JDBCTools.getConnection();
String sql = "INSERTINTO customers(name, email, birth, picture)"
+ "VALUES(?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"ABCDE");
preparedStatement.setString(2,"abcde@atguigu.com");
preparedStatement.setDate(3,
new Date(new java.util.Date().getTime()));
InputStream inputStream = newFileInputStream("Hydrangeas.jpg");
preparedStatement.setBlob(4, inputStream);
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCTools.releaseDB(null,preparedStatement, connection);
}
}
最后附上链接http://blog.csdn.net/cx8122389/article/details/62422249