JDBC是一组规范:接口。用于java程序与数据库之间的通信(中间商算是)。
加载与注册 JDBC 驱动
基础版:
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.添加jar
//2.加载驱动
Class.forName("com.mysql.jdbc.Driver");//初始化给定的类
//3.通过驱动管理器获取连接对象
//url是为了防止数据库和代码不在同一台机器 jdbc:mysql:// 是固定的 localhost 本地 ip地址 不同 java:你的数据库名称
String url = "jdbc:mysql://localhost:3306/java";
String user = "root";
String pwd = "123456";
Connection connection = DriverManager.getConnection(url, user, pwd);
System.out.println("connection = " + connection);
}
进行插入数据:编写的sql语句?作为占位符 ,建立预处理命令对象后进行参数的填空,最后执行executeUpdate()操作(参数已填充完毕),最后释放资源。
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.通过驱动管理器获取连接对象 Url表示和数据库通信的地址 如果url中需要带参数,则需要使用?进行连接 如果需要使用多个参数,从第二个参数开始使用&
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java?userSSL=false&useUnicode=true&characterEncoding=utf-8", "root", "123456");
//3.编写sql语句 ?作为占位符
String sql = "insert into fruitdb values(0,?,?,?,?)";
//4.创建预处理命令对象 PreparedStatement(是接口) 把 sql语句装进去然后给DB
PreparedStatement psmt = connection.prepareStatement(sql);
//5.填充参数 1234 为第几个问号
psmt.setString(1,"banana");
psmt.setInt(2,15);
psmt.setInt(3,100);
psmt.setString(4,"very delicious");
//6.执行更新(增删改),返回影响行数
int count = psmt.executeUpdate();
System.out.println(count>0?"添加成功":"添加失败!");
//7.释放资源(关闭连接,先关闭psmt,后关闭con)
psmt.close();
connection.close();
}
修与改:除了sql语句不同外,其他的和插入步骤全部一致,返回值也一致
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Fruit fruit = new Fruit(4,"grapy",15,30);
Class.forName("com.mysql.jdbc.Driver");
//2.通过驱动管理器获取连接对象 Url表示和数据库通信的地址 如果url中需要带参数,则需要使用?进行连接 如果需要使用多个参数,从第二个参数开始使用&
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java?userSSL=false&useUnicode=true&characterEncoding=utf-8", "root", "123xwt");
//3.编写sql语句 ?作为占位符
String sql = "update fruitdb set fname = ?,price=?,fcount = ? where fid = ?";
//String sql = "delete from fruitdb where fid = ?";
PreparedStatement psmt = connection.prepareStatement(sql);
psmt.setString(1,fruit.getFname());
psmt.setInt(2,fruit.getPrice());
psmt.setInt(3,fruit.getFcount());
psmt.setInt(4,fruit.getFid());
int count = psmt.executeUpdate();
System.out.println(count>0?"添加成功":"添加失败!");
psmt.close();
connection.close();
}
查询操作:返回的是结果集ResultSet,同时需要遍历使用get,数字代表列号(从1开始),字符串代表结果集的列名(SQL语句中是别名用别名),执行的是.executeQuery(),释放资源时,结果集也要释放。
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.通过驱动管理器获取连接对象 Url表示和数据库通信的地址 如果url中需要带参数,则需要使用?进行连接 如果需要使用多个参数,从第二个参数开始使用&
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java?userSSL=false&useUnicode=true&characterEncoding=utf-8", "root", "123xwt");
//3.编写sql语句 ?作为占位符
String sql = "select * from fruitdb ";
//4.创建预处理命令对象 PreparedStatement(是接口) 把 sql语句装进去然后给DB
PreparedStatement psmt = connection.prepareStatement(sql);
//5.执行查询,返回结果集
ResultSet rs = psmt.executeQuery();
ArrayList<Fruit> fruitArrayList = new ArrayList<>();
//6,解析结果集
while (rs.next()){
int fid = rs.getInt(1);//1表示当前行的第一列
String fname = rs.getString("fname");//getString/getInt 可以接结果集的列名(SQL语句中是别名用别名)
int price = rs.getInt(3);
int fcount = rs.getInt(4);
String fremark = rs.getString("remark");
Fruit fruit = new Fruit(fid, fname, price, fcount, fremark);
fruitArrayList.add(fruit);
}
//7.释放资源(关闭连接,先关闭psmt,后关闭con)
rs.close();//结果集也需要关闭
psmt.close();
connection.close();
fruitArrayList.forEach(System.out::println );
}
public interface FruitDao {
//查询库存列表
List<Fruit> getFruitList();
//新增库存
boolean addFruit(Fruit fruit);
//修改库存
boolean updateFruit(Fruit fruit);
//根据名称查询特定库存
Fruit getFruitByFname(String fname);
//删除特定库存记录
boolean delFruit(String fname);
}

实现类实现接口:
public class FruitDaoImpl extends BaseDao<Fruit> implements FruitDao {
@Override
public List<Fruit> getFruitList() {
String sql = "select * from fruitdb ";
return super.executeQuery(sql);
}
@Override
public boolean addFruit(Fruit fruit) {
String sql = "insert into fruitdb values(0,?,?,?,?)";
return super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark())>0;
}
@Override
public boolean updateFruit(Fruit fruit) {
String sql = "update fruitdb set fcount = ? where fid = ?";
return super.executeUpdate(sql,fruit.getFcount(),fruit.getFid())>0;
}
@Override
public Fruit getFruitByFname(String fname) {
String sql = "select * from fruitdb where fname= ?";
return super.load(sql,fname);
}
@Override
public boolean delFruit(String fname) {
String sql = "delete from fruitdb where fname like ?";
return super.executeUpdate(sql,fname)>0;
}
}
将所有的操作写在父类方法里,实现类继承,然后传入相关sql语句,调用相关方法:
public abstract class BaseDao<T> {
protected ResultSet rs;
protected PreparedStatement psmt;
protected Connection connection;
protected String URL= "jdbc:mysql://localhost:3306/java?userSSL=false&useUnicode=true&characterEncoding=utf-8";
protected String DRIVER= "com.mysql.jdbc.Driver";
protected String USER = "root";
protected String PWD = "123xwt";
//T的class对象
private Class entityClass;
public BaseDao(){
//获得父类的泛型类(com...父类名<泛型类型>
Type genericSuperclass = getClass().getGenericSuperclass();
//ParameterizedType 参数化类型,即泛型 getActualTypeArguments()过的参数化类型的数组,泛型可能有多个
Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
//获取到的<T>的真实的类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
protected Connection getCon(){
try {
Class.forName(DRIVER);
connection = DriverManager.getConnection(URL, USER, PWD);
return connection;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//执行更新,返回影响行数 Object... params 不定长参数,可传入多个参数
protected int executeUpdate(String sql,Object... params){
boolean insertFlag = false;//为了获取自增列
//trim() 去除首尾空格 toUpperCase() 全部转换为大写 startsWith() 以什么开头
insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
try {
connection = getCon();
if (insertFlag){
psmt = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
} else {
psmt = connection.prepareStatement(sql);
}
setParams(psmt,params);
int count = psmt.executeUpdate();
rs = psmt.getGeneratedKeys();//insert 语句才会这样执行
if (rs.next()){
return ((Long)rs.getLong(1)).intValue(); //返回自增列值
}
return count;
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if (psmt!=null){
psmt.close();
}
if (connection!=null&&!connection.isClosed()){
connection.close();
}}
catch(SQLException e){
e.printStackTrace();
}
}
return 0;}
//给预处理命令对象设置参数
private void setParams(PreparedStatement psmt,Object... params) throws SQLException {
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
psmt.setObject(i + 1, params[i]);
}
}
}
//通过反射技术给obj对象的property属性赋propertyvalue值
private void setValue(Object obj,String property,Object propertyValue){
Class clazz = obj.getClass();
try {
//获取property这个字符串对应的属性名﹐比如"fid"去找 obj对象中的 fid属性
Field field = clazz.getDeclaredField(property);
if (field!=null){
field.setAccessible(true);//强制访问,防止是private
field.set(obj,propertyValue);
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//执行查询,返回查询结果集
protected ArrayList<T> executeQuery(String sql,Object... params){
ArrayList<T> arrayList = new ArrayList<>();
try {
connection = getCon();
psmt = connection.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据︰描迹结果集数据的数据,简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()){
T entity = (T) entityClass.newInstance();
for (int i = 0; i < columnCount; i++) {
String columnName = rsmd.getColumnName(i + 1);//获取列名
Object columnValue = rs.getObject(i + 1); //获取值
setValue(entity,columnName,columnValue);
};
//Fruit fruit = new Fruit(fid, fname, price, fcount, fremark);
arrayList.add(entity);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} finally {
try {
if (psmt!=null){
psmt.close();
}
if (connection!=null&&!connection.isClosed()){
connection.close();
}}
catch(SQLException e){
e.printStackTrace();
}
}
return arrayList;
}
//执行查询,返回单个实体对象
protected T load(String sql,Object... params){
try {
connection = getCon();
psmt = connection.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据︰描迹结果集数据的数据,简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if (rs.next()){
T entity = (T) entityClass.newInstance();//新建一个实例
for (int i = 0; i < columnCount; i++) {
String columnName = rsmd.getColumnName(i + 1);//获取列名
Object columnValue = rs.getObject(i + 1); //获取值
setValue(entity,columnName,columnValue);
}
return entity;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} finally {
try {
if (psmt!=null){
psmt.close();
}
if (connection!=null&&!connection.isClosed()){
connection.close();
}}
catch(SQLException e){
e.printStackTrace();
}
}
return null;
}
//执行复杂查询(聚合函数等) 返回统计结果
protected Object[] executeComplexQuer(String sql,Object... params){
try {
connection = getCon();
psmt = connection.prepareStatement(sql);
setParams(psmt,params);
rs = psmt.executeQuery();
//通过rs可以获取结果集的元数据
//元数据︰描迹结果集数据的数据,简单讲,就是这个结果集有哪些列,什么类型等等
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
Object[] columnValueArr = new Object[columnCount];
if (rs.next()){
for (int i = 0; i < columnCount; i++) {
Object columnValue = rs.getObject(i + 1); //获取值
columnValueArr[i] = columnValue;
}
return columnValueArr;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (psmt!=null){
psmt.close();
}
if (connection!=null&&!connection.isClosed()){
connection.close();
}}
catch(SQLException e){
e.printStackTrace();
}
}
return null;
}
}
我们把连接和释放写入一个方法里:
protected Connection getCon(){
try {
Class.forName(DRIVER);
connection = DriverManager.getConnection(URL, USER, PWD);
return connection;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
return null;
}
当使用泛型时(可能时多个泛型),我们获取具体的类名 actualTypeArguments[0]获取第一个泛型的类名
public BaseDao(){
//获得父类的泛型类(com...父类名<泛型类型>
Type genericSuperclass = getClass().getGenericSuperclass();
//ParameterizedType 参数化类型,即泛型 getActualTypeArguments()过的参数化类型的数组,泛型可能有多个
Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
//获取到的<T>的真实的类型
Type actualType = actualTypeArguments[0];
try {
entityClass = Class.forName(actualType.getTypeName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
同时将预处理命令对象设置参数抽成一个方法:这里就是setObject
//给预处理命令对象设置参数
private void setParams(PreparedStatement psmt,Object... params) throws SQLException {
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
psmt.setObject(i + 1, params[i]);
}
}
}
进行自增列查询时:预处理命令对象的创建需要使用额外的参数 Statement.RETURN_GENERATED_KEYS
psmt = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
同时获取时:
rs = psmt.getGeneratedKeys();//insert 语句才会这样执行
if (rs.next()){
return ((Long)rs.getLong(1)).intValue(); //返回自增列值
}
进行批处理:要执行批处理任务,需要添加一个参数:rewriteBatchedStatements=true
同时使用.addBatch(); 和executeBatch(); 当任务太多时,我们可以分批执行 用if
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
//2.通过驱动管理器获取连接对象 如果要执行批处理任务,需要添加一个参数:rewriteBatchedStatements=true
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java?userSSL=false&useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true", "root", "123xwt");
//3.编写sql语句 ?作为占位符
String sql = "insert into fruitdb values(0,?,?,?,?)";
//4.创建预处理命令对象 PreparedStatement(是接口) 把 sql语句装进去然后给DB
PreparedStatement psmt = connection.prepareStatement(sql);
for (int i = 0; i <10 ; i++) {
psmt.setString(1,"banana"+i);
psmt.setInt(2,15);
psmt.setInt(3,100);
psmt.setString(4,"very delicious");
//添加到批中
psmt.addBatch();
if (i%1000==0){//如果任务较多,可以分批次执行,每次执行完,清空任务队列
psmt.executeBatch();
psmt.clearBatch();
}
}
int[] count = psmt.executeBatch();
for (int i = 0; i < count.length; i++) {
System.out.println(count[i]);
}
//7.释放资源(关闭连接,先关闭psmt,后关闭con)
psmt.close();
connection.close();
}
使用德鲁伊Druid连接池:
//1.连接池中被close的对象并没有被真正关闭,而是将状态设置为空闲状态,然后放回池子,下次获取连接对象,这个对象会被重复利用 //2.没有被close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的
//读取外部的配置文件
public class Demo05Druid {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
InputStream is = Demo05Druid.class.getClassLoader().getResourceAsStream("jdbc2.properties");
properties.load(is);//加载属性文件
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//1.连接池中被close的对象并没有被真正关闭,而是将状态设置为空闲状态,然后放回池子,下次获取连接对象,这个对象会被重复利用
//2.没有被close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的
Connection connection = dataSource.getConnection();//建立连接对象,可以建立多个连接对象
System.out.println(connection);
}
}
创建属性配置文件,写入参数:
driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/java?userSSL=false&useUnicode=true&characterEncoding=utf-8
username = root
password = 123456
initialSize = 2
maxActive = 5
maxWait = 5000