##JDBC Java DataBase Connectivity: Java数据库连接
一套连接数据库的API(Application Program Interface 应用程序编程接口)
*为什么使用JDBC:
因为java编程语言有可能需要连接多种数据库, 如果没有JDBC,java程序员连接每一种数据库都需要学习一条对应方法调用, 为了避免java程序员学习多套操作数据的方法,sun公司提供jdbc接口, 让各个数据厂商根据此接口写实现类(驱动), 这样的话java程序员只需要掌握jdbc接口中的方法的调用, 就可以访问任何数据,而且换数据时代码不需要做任何的改动
*JDBC连接Mysql数据库:
在maven里下载第三方的jar包--mysqlconnector--下载jar包(连接数据库)
将jar包放到项目里
把jar包集成到项目中(正常jdk里面没有这些类 是第三方提供的)
*连接数据库:
1.加载驱动 2.获取连接 3.获取操作数据库对象 4.操作数据库
代码:
//加载数据库 Connection conn = null; try { //1.加载驱动 在com.mysql.jdbc这个包下面有一个Driver类 Class.forName("com.mysql.jdbc.Driver");
Jdbc程序中的Connection,它用于代表数据库的链接, Collection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的, 这个对象的常用方法: • 方法 描述 createStatement() 创建向数据库发送sql的statement对象。 prepareStatement(sql) 创建向数据库发送预编译sql的PrepareSatement对象。 prepareCall(sql) 创建执行存储过程的callableStatement对象。 setAutoCommit(boolean autoCommit) 设置事务是否自动提交。 commit() 在链接上提交事务。 rollback() 在此链接上回滚事务。
//2.获取链接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
url:连接哪个数据库(数据库在哪) jdbc:mysql: 连接数据库的协议 • dbc:mysql://localhost:3306/test 连接本机的数据库 3306:默认端口号 ======== 常用数据库URL地址的写法: Oracle:jdbc:oracle:thin:@localhost:1521:shen SqlServer:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=shen MySql:jdbc:mysql://localhost:3306/shen ======== • user:数据库用户名 password:数据库密码
//3.获取操作数据库的对象
Statement s = conn.createStatement(); String SQL = "create table t_jdbctest(id int(11),name varchar(12))"; s.execute(SQL);//返回值为布尔类型, true表示有结果返回 false表示没有返回 它可以执行所有的sql语句
Jdbc程序中的Statement对象用于向数据库发送SQL语句,创建方法为: • Statement st = conn.createStatement(); Statement对象常用方法: • 方法 含义 executeQuery(String sql) 用于向数据发送查询语句。 返回值类型ResultSet,里面装着查询到的所有数据 • 可以通过以下两种方法查询: • 通获取数据库查询出来的字段的值(通过列名): int deptno= rs.getInt("deptno"); • 通过第几列获取数据库查询出来的值 : int deptno= rs.getInt(1); executeUpdate(String sql) 用于向数据库发送insert、update或delete语句 返回值为int类型,代表生效的行数 execute(String sql) 用于向数据库发送任意sql语句 返回值是布尔值类型,代表是否有结果集 true表示有结果返回 false表示没有返回 它可以执行所有的sql语句 addBatch(String sql) 把多条sql语句放到一个批处理中。 executeBatch() 向数据库发送一批sql语句执行。 execute(sql) 当执行数据库相关和表相关的SQL语句时使用该方法, executeUpdate(sql) 执行增删改相关SQL使用此方法, executeQuery(sql) 执行查询的sql语句时使用此方法, 返回值类型ResultSet,里面装着查询到的所有数据 可以通过以下两种方法查询: 通获取数据库查询出来的字段的值(通过列名) : int deptno= rs.getInt("deptno"); 通过第几列获取数据库查询出来的值 : int deptno= rs.getInt(1);
} catch (Exception e) { e.printStackTrace(); }finally { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
*PreperedStatement:
PreperedStatement是Statement的孩子,它的实例对象可以通过调用:
PreperedStatement st = conn.preparedStatement()
*比较:
相对于Statement对象而言PreperedStatement可以避免SQL注入的问题。 Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement 可对SQL进行预编译,从而提高数据库的执行效率。并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。
*获取结果:
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式,ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
1、获取行 ResultSet提供了对结果集进行滚动的方法: next():移动到下一行 Previous():移动到前一行 absolute(int row):移动到指定行 beforeFirst():移动resultSet的最前面。 afterLast() :移动到resultSet的最后面。 2、获取值 ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法: 获取任意类型的数据 getObject(int index) getObject(string columnName) 获取指定类型的数据,例如: getString(int index) getString(String columnName)
附加: 常用数据类型转换: SQL类型 Jdbc对应方法 返回类型 bit(1),bit(n) getBoolean,getBytes() Boolean,byte[] tinyint getByte() Byte smallint getShort() Short int getInt Int bigint getLong() Long char,varchar,longvarchar getString String text(clob) blob getClob(),getblob() Clob,blob date getDate() java.sql.Date time getTime() java.sql.Time timestamp getTimestamp java.sql.Timestamp
*关闭资源:
Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
注意:为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
*通过配置文件读取:
配置文件:不改源代码 就可以修改数据库信息 通过流的形式读出来 想改的时候直接改配置文件就行了
1.创建类资源路径文件夹 2.需要把配置文件放到类资源路径中 3.在项目下创建一个普通的文件夹--将这个普通的文件夹改成resource文件夹 4在里面创建一个以.properties结尾的文件夹(以k-v的形式存储)
*前面加db. 是为了以后读数据的时候不读串 代表着数据库的东西
5.用流来读取配置文件中的信息 替换掉之前写的语句(url driver 等等)
*用Properties来读取配置文件(key-value) Properties p=new Properties(); InputStream is=Demo01.class.getClassLoader().getResourceAsStream("db.properties"); 返回一个is对象 类加载器 调用这个对象 传配置文件 传配置文件时直接写相对路径就行了
*写一个工具类来连接数据库:
工具类: 每一次链接都是重复的 所以写一个工具类来提供数据库连接 提供一个获取数据库连接的方法 返回连接 *封装连接数据库的方法--配置文件获取数据库信息--
###数据库连接池DBCP(DataBaseConnectionPool)
*解决依赖jar包的问题:用maven工程来管理jar包 pom文件就是管理jar包的 把路径拷进去 file--settings--搜索maven
-
为什么使用连接池: 如果不使用连接池,一万次业务请求需要和数据库服务器建立1万次连接,需要一万次的开关连接,这样会非常浪费资源,通过数据库连接池可以将连接重用,避免资源的浪费
-
如何使用连接池:
-
引入dbcp相关jar包
<dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>
-
通过以下代码创建数据库连接池对象并获取连接
//创建连接池对象 BasicDataSource ds = new BasicDataSource(); //设置连接信息 ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/newdb3"); ds.setUsername("root"); ds.setPassword("root"); //设置初始连接数量 ds.setInitialSize(3); //设置最大连接数量 ds.setMaxActive(5); //获取连接 异常抛出 Connection conn = ds.getConnection(); System.out.println(conn); ###实现登录流程 create table user2(id int primary key auto_increment,username varchar(10),password varchar(10)); insert into user2 values(null,'zhangsan','123456'),(null,'lisi','admin'); select * from user;
-
-
登录的SQL: select count(*) from user where username='lisi' and password='admin';
select count(*) from user where username='asdoifjs' and password='' or '1'='1'
-
SQL注入:用户输入值的地方,输入进去了SQL语句导致原SQL语句的逻辑发生改变称为SQL注入。
###PreparedStatement 预编译的SQL执行对象
-
好处:
-
代表结构整洁不用拼接字符串,避免了拼接出错
-
可以避免SQL注入: 因为在创建SQL执行对象时就已经对SQL语句进行编译,将SQL的逻辑锁死,不会被用户输入的内容影响原有逻辑。
###批量执行
-
-
将多条SQL语句的多次数据传输,合并成一次数据传输,目的为了提高执行效率
-
是Statement的方法
-
stat.addBatch(sql1); stat.addBatch(sql2); stat.addBatch(sql3); //执行批量操作 stat.executeBatch();
-
for (int i = 1; i <= 100; i++) { ps.setInt(1, 10000+i); ps.setString(2, "name"+i); //添加到批量操作 ps.addBatch();//PreparedStatement 批量操作
//为了避免内存溢出 每20次执行一次 if(i%20==0) { //执行批量操作 ps.executeBatch(); } }
#### ###分页查询 limit (n-1)*m,m
-
Scanner sc = new Scanner(System.in); System.out.println("请输入查询的页数"); int page = sc.nextInt(); System.out.println("请输入查询的条数"); int count = sc.nextInt(); //1. 获取连接 //2. 创建sql语句 //3. 创建预编译的sql执行对象 //4. 将用户输入的页数和条数替换掉sql里面的? //5. 执行SQL语句 得到ResultSet //6. 遍历resultset //7. 取出员工编号和姓名 在控制台输出 //获取连接 并自动关闭 try (Connection conn = DBUtils.getConn()) {//jdk7的约束 String sql = "select * from emp limit ?,?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, (page-1)*count); ps.setInt(2, count); ResultSet rs = ps.executeQuery(); while(rs.next()) { int empno = rs.getInt("empno"); String name = rs.getString("ename"); System.out.println(name+":"+empno); } } catch (Exception e) { e.printStackTrace(); }
###获取自增主键的值
create table team(id int primary key auto_increment,name varchar(10)) charset=utf8; create table player(id int primary key auto_increment,name varchar(10),team_id int) charset=utf8;
String sql = "insert into user values" + "(null,?,?)"; //创建SQL执行对象 并设置获取自增主键值 PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setString(1, "libai"); ps.setString(2, "123456"); ps.executeUpdate(); //获取自增的主键值 ResultSet rs = ps.getGeneratedKeys(); while(rs.next()) { //由于只有一个返回的数值 int key = rs.getInt(1); System.out.println(key); }
###获取元数据
-
数据库的元数据 //获取数据库元数据对象 DatabaseMetaData dbmd = conn.getMetaData(); System.out.println("数据库名:"+ dbmd.getDatabaseProductName()); System.out.println("驱动版本:"+ dbmd.getDriverVersion()); System.out.println("用户名:"+ dbmd.getUserName());
-
表的元数据
//获取表的元数据 Statement stat = conn.createStatement(); String sql = "select * from emp"; ResultSet rs = stat.executeQuery(sql); //得到表元数据对象 ResultSetMetaData rsmd = rs.getMetaData(); //获取表字段数量 int count = rsmd.getColumnCount(); //获取表字段名称和类型 for (int i = 0; i < count; i++) { String name = rsmd.getColumnName(i+1); String type = rsmd.getColumnTypeName(i+1); System.out.println(name+":"+type); }