JDBC最详数据库连接
Java数据库连接(Java DataBase Connectivity), 简称: JDBC
JDBC是Java连接数据库的基石,Hibernate、MyBatis等只是更好的封装了数据库
1. JDBC介绍
JDBC是一个独立于特定的数据库管理系统, 通用的SQL数据库存取和操作的公共接口,规范了不同数据库之间被Java访问需要用不同的接口
2. JDBC程序编写步骤
补充:ODBC(Open Database Connectivity,开放式数据库连接),是微软在Windows平台下推出的。使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。
3. 获取数据库的连接
3.1 导入Driver接口的包
-
复制jar文件,进入idea
-
在module下创建lib目录,将文件粘贴到lib目录下
-
右击jar文件,点击Add as Library
3.2 加载与注册JDBC驱动
- Driver类通常都不需用手动注册,在Driver类中,只要加载了类就会有今天代码块自动注册
- 加载驱动用Class调用静态方法forName(),向其传入要加载的JDBC驱动的类名
Class.forName(“com.mysql.jdbc.Driver”);
3.3 URL
JDBC的URL用于一个被注册的驱动程序, 驱动程序管理器通过这个URL正确的连接数据库
URL由三部分组成: 协议:子协议://主机地址:端口号:数据库名
- 协议: 通常都是jdbc
- 子协议: 用于标识数据库驱动程序
MySQL的连接URL编写方式: - jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
- jdbc:mysql://localhost:3306/atguigu
- jdbc:mysql://localhost:3306/atguigu**?useUnicode=true&characterEncoding=utf8**(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
- jdbc:mysql://localhost:3306/atguigu?user=root&password=123456
3.4 用户名和密码
- user,password可以用“属性名=属性值”方式告诉数据库
- 可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接
3.5 创建配置文件存储配置
- 在module下,右击创建File
- 用户名、密码、URL、驱动都写入配置文件,如后面需要修改方便修改
4. 连接数据库
public static void main(String[] args) throws Exception {
//1.创建输入流,指明配置文件路径
FileInputStream fis = new FileInputStream("D:\\Java-project\\jdbc_1\\jdbc\\propertiesjdbc");
//2.创建Properties实例
Properties pro = new Properties();
//3.加载配置文件
pro.load(fis);
//4.获取配置文件的每个值
String user = pro.getProperty("user");
String password = pro.getProperty("password");
String url = pro.getProperty("url");
String drivermanager = pro.getProperty("drivermanager");
//5.加载驱动程序
Class.forName(drivermanager);
//6.测试连接是否成功
Connection con = DriverManager.getConnection(url, user, password);
System.out.println(con);
}
5. 使用PreparedStatement实现CRUD操作
- 数据库连接被赋予发送SQL命令语句,并接收数据库返回的结果
- PreparedStatement: SQL语句被预编译在此对象中, 可以对此对象多次高效的执行语句
- PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
5.1 使用PreparedStatement实现查询操作
// 通用的针对于不同表的查询:返回一个对象 (version 1.0)
public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,得到PreparedStatement对象
ps = conn.prepareStatement(sql);
// 3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
// 4.执行executeQuery(),得到结果集:ResultSet
rs = ps.executeQuery();
// 5.得到结果集的元数据:ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 6.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {// 遍历每一个列
// 获取列值
Object columnVal = rs.getObject(i + 1);
// 获取列的别名:列的别名,使用类的属性名充当
String columnLabel = rsmd.getColumnLabel(i + 1);
// 6.2使用反射,给对象的相应属性赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7.关闭资源
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
6. ResultSet与ResultSetMetaData
ResultSet
- ResultSet是PreparedStatement的查询后返回的结果集,结果集不包含数据表中的字段名
- ResultSet 维护了一个指向当前数据的游标,游标在第一行上方, 当调用next()方法时,检车下一行是否有效,若有效,则返回true,进入下一行
ResultSetMetaData
- ResultSetMetaData是ResultSet返回数据表中的元数据
- 且只包含数据表中字段名
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
- ResultSetMetaData meta = rs.getMetaData();
-
getColumnName(int column):获取指定列的名称
-
getColumnLabel(int column):获取指定列的别名
-
getColumnCount():返回当前 ResultSet 对象中的列数。
-
getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
-
getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
-
isNullable(int column):指示指定列中的值是否可以为 null。
-
isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
-
7. 创建DAO的通用的数据库连接方式
DAO(Data Access Object) 数据访问对象
7.1 DAO类: 通用增删改查操作
/**
* @author gdcho
* @create -08-18
*/
public class DataAccessObject {
//①、DAO类, 通用的针对不同表的查询: 返回全部
public static <E> List<E> getAllRow(Connection con, String sql,Class<E> clazz,Object ...obj){
PreparedStatement ps = null;
ResultSet rs = null;
List<E> list = null;
try {
// 1.获取数据库连接
// 2.预编译sql语句,得到PreparedStatement对象
ps = con.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
// 3.填充SQL语句的占位符
ps.setObject(i+1,obj[i]);
}
// 4.执行executeQuery(),得到结果集:ResultSet
rs = ps.executeQuery();
// 5.得到结果集的元数据:ResultSetMetaData
ResultSetMetaData md = rs.getMetaData();
//6.获取数据表中的字段数
int columnCount = md.getColumnCount();
//7.创建ArrayAList()实现类用于存储对象
list = new ArrayList<>();
//8.检车下一行是否有效,若有效,则返回true,进入下一行
while (rs.next()) {
//9.使用泛型对象获取实例
E e = clazz.newInstance();
//10.遍历每个字段的元素
for (int i = 0; i < columnCount; i++) {
//11.获取当前字段的元素
Object columnVal = rs.getObject(i + 1);
//12.获取当前字段的字段名
String columnLabel = md.getColumnLabel(i + 1);
//13.通过反射把获取的字段名与属性名匹配
Field field = e.getClass().getDeclaredField(columnLabel);
//14.设置安全访问,使得能访问private的属性
field.setAccessible(true);
//15.存储元素
field.set(e,columnVal);
}
//16.将实例对象存储进list
list.add(e);
}
//17.返回list 便于调用时候遍历list里的对象
return list;
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
Connect.clseResource(null,rs,ps);
}
return null;
}
//②、DAO类, 通用的针对不同表的查询: 返回单行
public static <E> E getOneRow(Connection con, String sql, Class<E> clazz, Object... obj){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i+1,obj[i]);
}
rs = ps.executeQuery();
ResultSetMetaData md = rs.getMetaData();
int columnCount = md.getColumnCount();
while (rs.next()) {
E e = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object columnVal = rs.getObject(i + 1);
String columnLabel = md.getColumnLabel(i + 1);
Field field = e.getClass().getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(e,columnVal);
}
return e;
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
Connect.clseResource(null,rs,ps);
}
return null;
}
//③、DAO类, CUDR操作
public static void toUpdate(Connection con,String sql,Object ...obj){
PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i+1,obj[i]);
}
ps.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
Connect.clseResource(null,ps);
}
}
//④、DAO类, 查询单行单列
public static <E> E getCount(Connection con,String sql,Object ... obj){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i+1,obj[i]);
}
rs = ps.executeQuery();
while (rs.next()) {
return (E) rs.getObject(1);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
Connect.clseResource(null, rs,ps);
}
return null;
}
}
7.2 创建DAO通用操作接口
接口后需创建一个泛型, 作为实现类需要指明的泛型
//接口后创建一个泛型
public interface DataAccessObjectInterface<E> {
//查询一行
E toQueryOneRow(Connection con,int id);
//查询多行
List<E> toQueryAllRow(Connection con);
//增加一行
void insertOneRow(Connection con,E e);
//删除一行
void deleteOneRow(Connection con, int id);
//查询总数
Object toCount(Connection con);
//查询最大值
Object toMaxValue(Connection con);
}
针对单独的数据表创建JavaBean
JavaBean类中的属性名一 一对应数据表中的字段名
public class Customers {
private long id;
private String name;
private String email;
private Date birth;
public Customers(long id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public Customers() {
}
@Override
public String toString() {
return "Customers{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + ", birth=" + birth + '}';
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
7.3 创建DAO实现类
实现类必须继承DAO类与实现DAO通用操作接口,并重写接口中的抽象方法
public class DataAccessObjectImpl extends DataAccessObject implements DataAccessObjectInterface<Customers>{
@Override
public Customers toQueryOneRow(Connection con, int id) {
String sql = "select id,name,email,birth from customers where id = ?";
return getOneRow(con,sql,Customers.class,id);
}
@Override
public List<Customers> toQueryAllRow(Connection con) {
String sql = "select id,name,email,birth from customers";
return getAllRow(con, sql, Customers.class);
}
@Override
public void insertOneRow(Connection con, Customers cust) {
String sql = "insert into customers(id,name,email,birth) values(?,?,?,?)";
toUpdate(con,sql,cust.getId(),cust.getName(),cust.getEmail(),cust.getBirth());
}
@Override
public void deleteOneRow(Connection con, int id) {
String sql = "delete from customers where id = ?";
toUpdate(con,sql,id);
}
@Override
public Object toCount(Connection con) {
String sql = "select count(*) from customers";
return getCount(con,sql);
}
@Override
public Object toMaxValue(Connection con) {
String sql = "select max(birth) from customers";
return getCount(con,sql);
}
}
7.5 测试实现类的方法
public class DataAccessObjectImplTest {
public static void main(String[] args) {
Connection con = null;
try {
DataAccessObjectImpl dataAccessObject = new DataAccessObjectImpl();
con = Connect.getConnection();
//查询总数
Object o = dataAccessObject.toCount(con);
System.out.println("COUNT("+o+")");
//查询最大值
Object o1 = dataAccessObject.toMaxValue(con);
System.out.println("MAX_BIRTH("+o1+")");
System.out.println();
//查询多行
List<Customers> list = dataAccessObject.toQueryAllRow(con);
list.stream().forEach(System.out::println);
System.out.println();
//查询单行
Customers cust = dataAccessObject.toQueryOneRow(con, 5);
System.out.println(cust);
//插入一行
Date date = new Date();
Customers doctor = new Customers(24, "Doctor", "doc@126.com", date);
dataAccessObject.insertOneRow(con,doctor);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
Connect.clseResource(con,null,null);
}
}
}