一、jdbc连接数据库

1.数据库驱动,jdbc连接数据库

什么是jdbc?

JDBC代表Java数据库连接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API,换句话说:JDBC是用于在Java语言编程中与数据库连接的API

数据库驱动

我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件

例如:mysql-connector-java-5.1.46.jar

在这里插入图片描述

2.JDBC 常用接口

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”);

常用方法:

    • createStatement():创建向数据库发送sql的statement对象。
    • prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
    • prepareCall(sql):创建执行存储过程的callableStatement对象。
    • setAutoCommit(boolean autoCommit):设置事务是否自动提交。
    • commit() :在链接上提交事务。
    • rollback() :在此链接上回滚事务。

3.Statement接口

用于执行静态SQL语句并返回它所生成结果的对象。

三种Statement类:

    • Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
    • PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
    • CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。

常用Statement方法:

    • execute(String sql):运行语句,返回是否有结果集
    • executeQuery(String sql):运行select语句,返回ResultSet结果集。
    • executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
    • addBatch(String sql) :把多条sql语句放到一个批处理中。
    • executeBatch():向数据库发送一批sql语句执行。

4.ResultSet接口

ResultSet提供检索不同类型字段的方法,常用的有:

    • getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
    • getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
    • getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
    • getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
    • getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。

ResultSet还提供了对结果集进行滚动的方法:

    • next():移动到下一行
    • Previous():移动到前一行
    • absolute(int row):移动到指定行
    • beforeFirst():移动resultSet的最前面。
    • afterLast() :移动到resultSet的最后面。

使用后依次关闭对象及连接:ResultSet → Statement → Connection

3.使用JDBC的步骤

加载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();
        }
    }
}
4.JDBC之DBUtils封装

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();
		}

	}

}
5.JDBC之统一DQL封装 ; + 反射

ResultSetMetaData 对象; 获取表的元数据;

public class JDBCExecuteUtils {

	/**
	 * 封装的通用的查询一条纪录的方法
	 * @param <T> 泛型
	 * @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 <T> 泛型
	 * @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对象,循环设置参数;

			}

		}

	}

}
6.使用连接池

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);
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐