什么是jdbc?
JDBC代表Java数据库连接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API,换句话说:JDBC是用于在Java语言编程中与数据库连接的API
数据库驱动
我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件
例如:mysql-connector-java-5.1.46.jar
1.Driver接口
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
装载MySql驱动:Class.forName(“com.mysql.jdbc.Driver”);
装载Oracle驱动:Class.forName(“oracle.jdbc.driver.OracleDriver”);
2.Connection接口
Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。
连接MySql数据库:
Connection conn = DriverManager.getConnection(“jdbc:mysql://host:port/database”, “user”, “password”);
连接Oracle数据库:
Connection conn = DriverManager.getConnection(“jdbc:oracle:thin:@host:port:database”, “user”, “password”);
连接SqlServer数据库:
Connection conn = DriverManager.getConnection(“jdbc:microsoft:sqlserver://host:port; DatabaseName=database”, “user”, “password”);
常用方法:
3.Statement接口
用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类:
常用Statement方法:
4.ResultSet接口
ResultSet提供检索不同类型字段的方法,常用的有:
ResultSet还提供了对结果集进行滚动的方法:
使用后依次关闭对象及连接:ResultSet → Statement → Connection
加载JDBC驱动程序 → 建立数据库连接Connection → 创建执行SQL的语句Statement → 处理执行结果ResultSet → 释放资源
1.注册驱动 (只做一次)
方式一:Class.forName(“com.MySQL.jdbc.Driver”);
推荐这种方式,不会对具体的驱动类产生依赖。
方式二:DriverManager.registerDriver(com.mysql.jdbc.Driver);
会造成DriverManager中产生两个一样的驱动,并会对具体的驱动类产生依赖。
2.建立连接
Connection conn = DriverManager.getConnection(url, user, password);
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
其他参数如:useUnicode=true&characterEncoding=utf8
3.创建执行sql语句的statement
//Statement
String id = "5";
String sql = "delete from table where id=" + id;
Statement st = conn.createStatement();
st.executeQuery(sql);
//存在sql注入的危险
//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录
//PreparedStatement 有效的防止sql注入(SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令)
String sql = “insert into user (name,pwd) values(?,?)”;
PreparedStatement ps = conn.preparedStatement(sql);
ps.setString(1, “col_value”); //占位符顺序从1开始
ps.setString(2, “123456”); //也可以使用setObject
ps.executeQuery();
4.处理结果集(ResultSet)
ResultSet rs = ps.executeQuery();
While(rs.next()){
rs.getString(“col_name”);
rs.getInt(1);
//…
}
5.释放资源
释放资源顺序与创建顺序相反
//数据库连接(Connection)非常耗资源,尽量晚创建,尽量早的释放 //都要加try catch 以防前面关闭出错,后面的就不执行了 try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (st != null) {
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
1.静态变量封装 : 如果需要需改数据库,需要修改源代码,重新编译;
此种方式,不使用配置文件(连接哪一个数据库直接写死,需要更换数据库需要修改代码)
public class DBUtils {
static final String URL="jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true";
static final String USER="root";
static final String PASSWORD="123456";
//加载驱动只会执行一次
static{
//加载驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//提供静态方法获取数据库连接(返回数据库连接对象)
public static Connection getconnection(){
try {
return DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//提供静态方法关闭结果集、数据库操作对象、连接对象
public static void close(Connection con,Statement stm,ResultSet rs){
closeResultset(rs);
closeStatement(stm);
closeConnection(con);
}
//关闭连接
private static void closeConnection(Connection con){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//关闭操作对象
private static void closeStatement(Statement stm){
try {
stm.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//关闭结果集
private static void closeResultset(ResultSet rs){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2.使用配置文件,将需要连接的数据库信息写在配置文件里,直接修改配置文件可实现连接不同的数据库
配置文件(jdbc.properties):
url=jdbc:mysql://127.0.0.1:3306/nz2001?rewriteBatchedStatements=true"
user=root
password=123456
java代码
public class DBUtilsPro {
// 将三个串存放在jdbc.properties配置文件
static String url;
static String user;
static String password;
static {
try {
// 从编译的根目录下面,去获取一个文件,把它转成输入流
InputStream is = DBUtilsPro.class.getClassLoader().getSystemResourceAsStream("jdbc.properties");
// 通过properties对象去读取配置文件的内容
Properties pt = new Properties();
pt.load(is);
url = pt.getProperty(url);
user = pt.getProperty(user);
password = pt.getProperty(password);
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e1) {
e1.printStackTrace();
}
}
// 提供静态方法获取数据库连接(返回数据库连接对象)
public static Connection getconnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
// 提供静态方法关闭结果集、数据库操作对象、连接对象
public static void close(Connection con, Statement stm, ResultSet rs) {
closeResultset(rs);
closeStatement(stm);
closeConnection(con);
}
// 关闭连接
private static void closeConnection(Connection con) {
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 关闭操作对象
private static void closeStatement(Statement stm) {
try {
stm.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 关闭结果集
private static void closeResultset(ResultSet rs) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ResultSetMetaData 对象; 获取表的元数据;
public class JDBCExecuteUtils {
/**
* 封装的通用的查询一条纪录的方法
* @param 泛型
* @param cls 实体类的字节码对象
* @param sql sql语句
* @param params sql语句对应的参数
* @return 查询到的一条纪录
* @throws Exception
*/
public static <T> T executeDQLByOne(Class<T> cls, String sql, Object... params) throws Exception{
List<T> list = executeDQL(cls,sql,params);
if(null != list && list.size() > 0) {
return list.get(0);
}
return null;
}
/**
* 封装的通用的查询的方法
* @param 泛型
* @param cls 实体类的字节码对象
* @param sql sql语句
* @param params sql语句对应的参数
* @return 查询到的集合
* @throws Exception
*/
public static <T> List<T> executeDQL(Class<T> cls, String sql, Object... params) throws Exception {
// 获取连接
Connection connection = DBUtils.getConnection();
// 获取我们的预处理对象
PreparedStatement ps = connection.prepareStatement(sql);
// 完成了设置参数的过程
setParams(ps, params);
// 执行查询,获取结果集
ResultSet rs = ps.executeQuery();
// 获取我们查询结果的表(字段)信息
ResultSetMetaData md = ps.getMetaData();
// 将查询的数据,封装到List里面来;
List<T> list = new ArrayList<T>();
T obj;
Field field;
String name;
Object value;
while (rs.next()) { // 遍历我们的记录行
obj = cls.newInstance();
for (int i = 1; i <= md.getColumnCount(); i++) {
name = md.getColumnLabel(i);// 就是我们的字段名称;
try {
field = cls.getDeclaredField(name); // 如果发生了异常,表示我们实体类里面,没有这个字段
field.setAccessible(true);
value = rs.getObject(name);
if (value == null) { // 不能一刀切;
int type = md.getColumnType(i); // 这个方法会返回我们当前列的类型;
// 如果符合我们的某个类型
if (checkType(type)) {
// value = 0;
continue;
}
}
//给对象的字段设置值
field.set(obj, value);
} catch (NoSuchFieldException e) {
}
}
list.add(obj); // 将我们一个实例,添加到集合里面
}
return list;
}
// 判断type是其中的某一个类型
private static boolean checkType(int type) {
return Types.INTEGER == type || Types.FLOAT == type || Types.DECIMAL == type || Types.DOUBLE == type
|| Types.REAL == type;
}
// 统一来执行DML语句;
public static boolean executeDML(String sql, Object... params) throws SQLException {
// 获取连接
Connection connection = DBUtils.getConnection();
// 获取我们的预处理对象
PreparedStatement ps = connection.prepareStatement(sql);
// 完成了设置参数的过程
setParams(ps, params);
// 执行sql语句,返回执行的结果
return ps.executeUpdate() > 0;
}
/**
* 给ps设置参数
*
* @param ps 预处理对象
* @param params 可变长度的数组
* @throws SQLException
*/
private static void setParams(PreparedStatement ps, Object... params) throws SQLException {
if (null != params) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]); // 给ps对象,循环设置参数;
}
}
}
}
C3P0 老牌
druid 阿里巴巴 监控,日志分析做的非常好;
HikariCP springboot默认连接池;
所谓的连接池,就是一个jar包(别人写好的一些Java代码,我们拿过来用就行了)
jar包:druid-1.1.9.jar
/**
* 获取我们的数据库连接
* 关闭我们的连接对象
* @author ls
*/
public class DBUtilsDataSource {
static DataSource dataSource; //连接池接口
static {
try {
//获取一个properties的文件
InputStream is = DBUtilsDataSource.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
properties.load(is);
//需要一个接口的实现者, 来给这个接口 赋值;
//思考一下,为什么我们还需要一个properties文件???
//我们连接数据库的驱动,连接串,用户名,密码,是我们给连接池,还是连接池自己就有了
//连接池根据这个配置文件来连接我们的数据库;
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 使用连接池的这个技术,来获取连接;
* 通过连接池来得到一个连接
* @return
*/
public static Connection getConnection() {
try {
//这里是使用连接池的方式来获取连接
return dataSource.getConnection(); //从连接池里面获取一个连接
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
// 提供静态方法,关闭我们的结果集; 关闭我们的 ps对象; 关闭我们的连接对象;
public static void close(Connection con, PreparedStatement ps, ResultSet rs) {
closeResultSet(rs); // 关闭结果集
closePreparedStatement(ps); // 关闭操作数据库的对象
closeConnection(con);// 关闭连接
}
private static void closeResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void closePreparedStatement(PreparedStatement ps) {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void closeConnection(Connection con) {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
项目中连接数据库使用的代码:
使用连接池连接数据库
public class DbUtils {
private static DruidDataSource druidDataSource;//连接池接口
static {
InputStream is = DbUtils.class.getClassLoader().getResourceAsStream("Druid.properties");
Properties pt = new Properties();
try {
pt.load(is);
is.close();
druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(pt);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection(){
Connection con=null;
try {
con= druidDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
//关闭资源
public static void close(ResultSet rs, Connection con, Statement st){
try {
if(rs!=null){
rs.close();
}
if (con!=null){
con.close();
}
if(st!=null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
将获取数据库查询到数据通过反射方式封装为实体类,并以集合的形式返回
public class DataUtils {
/* 返回所有的数据
*/
public static <T>List<T> getAll(Class<T> clazz, ResultSet resultSet) throws Exception {
List<T> list=new ArrayList<>();
//使用while循环遍历每一行元素
while(resultSet.next()){
//生成一个实例,数据的每一行对应一个对象
T t=clazz.newInstance();
//返回数据库元数据信息,元数据信息包含列名(别名),列的类型,列的数量:resultSet.getMetaData()
ResultSetMetaData metaData = resultSet.getMetaData();
//获取列的数量
int columnCount = metaData.getColumnCount();
//获取每一列对应的的数据然后赋值给t的属性
for(int i=1;i<=columnCount;i++){
//获取别名
String label= metaData.getColumnLabel(i);
//获取属性类型(通过反射获取set方法,需要使用类型,set方法的参数类型和属性的类型是一致的)
Class<?> fieldType = clazz.getDeclaredField(label).getType();
//获取数据
Object obj = resultSet.getObject(i);
//获取属性的set方法
String setMethodName="set"+label.substring(0,1).toUpperCase()+label.substring(1);
Method setMethod = clazz.getMethod(setMethodName, fieldType);
//设置属性
setMethod.invoke(t,obj);
}
list.add(t);
}
return list;
}
在这一过程中,项目中需要引入的jar有:
数据库连接:mysql-connector-java-5.1.46.jar
线程池:druid-1.1.9.jar
如果后端将查询到的数据传前端,需要转json,则需要引入:fastjson-1.2.66.jar
//发送响应数据
protected void outPutJson(HttpServletResponse resp, Object obj) throws IOException {
//转成json数据格式字符串
String JSONStr= JSONObject.toJSONString(obj);
//设置MIME,并返回
resp.setContentType("application/json;charset=utf-8");
// 告诉浏览器,我这个谁都可以拿(设置响应头信息)
resp.setHeader("Access-Control-Allow-Origin", "*");
//获取输出流(网络流)
PrintWriter writer = resp.getWriter();
writer.write(JSONStr);
writer.flush();
writer.close();
}
用户注册、登录,密码采取加密处理(md5):commons-codec-1.14.jar
String password=req.getParameter("password");
/**
* 将密码做md5运算之后的字符串。
* 例如:1 -> c4ca4238a0b923820dcc509a6f75849b, 然后再数据库中去比较密文
*/
String md5Pwd = DigestUtils.md5Hex(password);
oJSONString(obj);
//设置MIME,并返回
resp.setContentType(“application/json;charset=utf-8”);
// 告诉浏览器,我这个谁都可以拿(设置响应头信息)
resp.setHeader(“Access-Control-Allow-Origin”, “*”);
//获取输出流(网络流)
PrintWriter writer = resp.getWriter();
writer.write(JSONStr);
writer.flush();
writer.close();
}
- 用户注册、登录,密码采取加密处理(md5):commons-codec-1.14.jar
```java
String password=req.getParameter("password");
/**
* 将密码做md5运算之后的字符串。
* 例如:1 -> c4ca4238a0b923820dcc509a6f75849b, 然后再数据库中去比较密文
*/
String md5Pwd = DigestUtils.md5Hex(password);