JDBC概述:
JDBC(java database connectivity):
是一个独立于特定数据库管理系统,通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准JAVA类库,使用这些类库可以以一种标准的方法,方便地访问数据库资源。
数据的持久化:把数据保存到可掉电式存储设备中以供以后使用,大多数情况下,特别是企业级应用,数据持久化意味着内存中的数据保存到硬盘上加以"固化",而持久化的实例过程大多通过各种关系数据库来完成。
持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件,XML数据文件中
JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC
获取数据库连接:
方式1:使用Driver接口
public class ConnectionTest {
public void testConnection1() throws SQLException {
Driver driver = new com.mysql.jdbc.Driver();
String url = null;
Properties info = null;
Connection conn = driver.connect(url,info);
System.out.println(conn);
}
}
方式2:对方式1的迭代(在如下的程序中不出现第三方的API,使得程序具有更好的可移植性)
public void testConnection1() throws Exception {
//获取Driver实现类的对象,使用反射
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)clazz.newInstance();
//提供要连接的数据库
String url = "jdbc:mysql://localhost:3306/test";
//提供连接需要的用户名和密码
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","");
//获取连接
Connection conn = driver.connect(url,info);
System.out.println(conn);
}
方式3:使用DriverManger替换Driver
public void testConnection1() throws Exception {
//获取Driver实现类的对象
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
//提供另外三个连接的基本信息
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "";
//注册驱动
DriverManager.registerDriver(driver);
//获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
方式4 可以只是加载驱动,不用显式的注册驱动了
public void testConnection1() throws Exception {
//提供另外三个连接的基本信息
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "";
//加载Driver
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
方式5(final版):将数据库连接需要的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接.
好处:
1.实现了数据与代码分离,实现了解耦
2.如果需要修改配置文件信息,可以避免程序重新打包。
public void testConnection1() throws Exception {
//读取配置文件中的4个基本信息
InputStream is = test4.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("users");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//加载驱动
Class.forName(driverClass);
//获取连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
使用Statement接口执行SQL语句:
存在拼串操作繁琐,SQL注入问题
SQL注入问题:是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据注入非法的SQL语句段或命令(如:SELECT USER,PASSWORD FROM USER_TABLE WHERE USER=‘A OR 1 =’ AND PASSWORD = 'OR ‘1’ = ‘1’ ),从而利用系统的SQL引擎完成恶意行为的做法。
避免SQL注入:使用PreparedStatement(从Statement扩展而来)取代Statement
在数据库中插入数据
public void testInsert() {
PreparedStatement ps = null;
Connection conn = null;
try {
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String user = pros.getProperty("users");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
Class.forName(driverClass);
conn = DriverManager.getConnection(url,user,password);
// System.out.println(conn);
String sql = "insert into customers(name,email,birth)values(?,?,?)";//?是占位符
ps = conn.prepareStatement(sql);
ps.setString(1,"哪吒");
ps.setString(2,"[email protected]");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1000-01-01");
ps.setDate(3,new Date(date.getTime()));
//执行操作
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}
//资源的关闭
finally {
try {
if (ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
通用的增删改操作:
public void update(String sql,Object ...args) {//sql中的占位符的个数与可变形参的长度相同
Connection conn = null;
PreparedStatement ps = null;
//通用的增删改操作
try {
//获取数据库的连接
conn = JDBCUtil.getConnection();
//预编译SQL语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0;i < args.length;i++){
ps.setObject(i+1,args[i]);//小心参数声明错误!
}
//执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}
finally {
//资源的关闭
JDBCUtil.closeResource(conn,ps);
}
}
针对customer表的通用的查询操作
@Test
public void testQueryForCustomers(){
String sql = "select id,name,birth,email from customers where id = ?";
Customer customer = queryForCustomers(sql,13);
System.out.println(customer);
}
public Customer queryForCustomers(String sql,Object...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集的列
int columnCount = rsmd.getColumnCount();
if (rs.next()){
Customer cust = new Customer();
//处理结果集一行数据中的每一个列
for (int i=0;i<columnCount;i++){
//获取列值
Object columnvalue = rs.getObject(i+1);
//获取每个列的列名
String columnName = rsmd.getColumnName(i+1);
//给cust对象指定的columnName属性,赋值为columnvalue
Field field = Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(cust,columnvalue);
}
return cust;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.closeResource(conn,ps,rs);
}
return null;
}
遇到Method xxx should be void 错误,是因为@test不能用于有返回值的方法,去掉@test
ORM编程思想(Object relational mapping):
一个数据表对于一个java类
表中的一条记录对应Java类的一个对象
表中的一个字段对应java的一个属性
方式1:
@Test
public void testGetInstance(){
String sql = "select id,name,email from customers where id = ?";
Customer customer = getInstance(Customer.class,sql,12);
System.out.println(customer);
String sql1 = "select order_id orderId,order_name orderName from `order` where order_id =?";
Order order = getInstance(Order.class,sql1,1);
System.out.println(order);
}
public <T> T getInstance(Class<T> clazz,String sql,Object...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
//获取每个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名: getColumnName()
//获取每个列的别名:getColumnLabel()
String columnLabel = rsmd.getColumnLabel(i + 1);
//
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.closeResource(conn, ps, rs);
}
return null;
}
方式2:
@Test
public void testGetForList(){
String sql = "select id,name,email from customers where id < ?";
List<Customer> list = getForList(Customer.class,sql,12);
list.forEach(System.out::println);
}
public <T> ArrayList<T> getForList(Class<T> clazz, String sql, Object...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<T>();
while (rs.next()) {
T t = clazz.newInstance();
//处理结果集一行数据中的每一个列:给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
//获取每个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名: getColumnName()
//获取每个列的别名:getColumnLabel()
String columnLabel = rsmd.getColumnLabel(i + 1);
//
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.closeResource(conn, ps, rs);
}
return null;
}
除了解决Statement的拼串、SQL问题之外,PreparedStatement还有哪些好处?
1.PreparedStatement操作Blob的数据,而Statement做不到
2.PreparedStatement可以实现更高效的批量操作