jdbc 编程
Posted 甘劭
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdbc 编程相关的知识,希望对你有一定的参考价值。
一. JDBC是什么及 工作原理
持久化概念 :把数据保存到可掉电的设备里面存储;
JDBC: Java Database Connectivity Java访问数据库的解决方案。
1. 使用java代码发送sql语句的技术,JDBC定义了一套标准接口,即访问数据库的通用API,不同的数据库厂商根据各自数据库的特点去实现这些接口。
2. 组成JDBC的2个包为java.sql和javax.sql。开发JDBC应用需要这2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
二. JDBC常用API
1. JDBC定义了一些接口
- 驱动管理 DriverManager
- 连接接口 Connection DatabasemetaData
- 语句对象接口 Statement PreparedStatement CallableStatement
- 结果集接口 ResultSet ResultMetaData
2. JDBC程序中的DriverManager用于加载驱动,并创建与数据库的连接,这个类的常用方法有:
DriverManager.registerDriver(new Driver()); DriverManager.getConnection(URL, user, password);
注意:在实际开发中不推荐采用registerDriver方法注册驱动,原因有二:
a. 查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象;
b. 程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
推荐方式:Class.forName("com.mysql.jdbc.Driver");
采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的Connection对象。
3.JDBC程序中的Connection对象用于代表数据库的连接,客户端与数据库所有交互都是通过Connection对象完成的.
createStatement(); //创建向数据库发送sql的statement对象 prepareStatement(sql); //创建向数据库发送预编译sql的prepareStatement对象。这个更常用 prepareCall(sql); //创建执行存储过程中的callableStatement对象 setAutoCommit(boolean autoCommit); //设置事物是否自动提交 ,在JDBC中,事务是默认提交的true. 必须先设置事务为手动提交false commit(); //在链接上提交事物 rollback(); //在此链接上回滚事物
4. JDBC程序中的Statement对象用于向数据库发送sql语句,Statement对象常用方法有:
executeQuery(String sql); //用于向数据库发送查询语句
executeUpdate(String sql); //用于向数据库发送insert,update或delete语句
execute(String sql); //用于向数据库发送任意sql语句
addBatch(String sql); //把多条sql语句放到一个批处理中
executeBath(); //向数据库发送一批sql语句执行
5. JDBC程序中的ResultSet对象用于代表sql语句的执行结果。
ResultSet封装执行结果时,采用的类似于表格的方式。ResultSet对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用该对象的next()方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
由于ResultSet用于封装执行结果,所以该对象提供的都是用于获取数据的get方法:
//获取任意类型的数据 getObject(int index); //index表示列号 getObject(String columnName); //columnName表示列名,建议用这种方法,更好维护
//获取指定类型的数据(int,String,Date等) getString(int index); getString(String columnName);
ResultSet除了提供get方法以外,还提供了对结果集进行滚动的方法:
next(); //移动到下一行 previous(); //移动到前一行 absolute(int row); //移动到指定行 beforeFirst(); //移动到resultSet的最前面 afterLast(); //移动到resultSet的最后面
三. JDBC访问数据库的工作过程: 贾琏欲执事
- 加载驱动,建立连接
- 创建语句对象
- 执行SQL语句
- 处理结果集
- 关闭连接
// 1.注册驱动的两种方法: // 方法一: // Driver driver = new com.mysql.jdbc.Driver(); //创建一个mysql驱动 // DriverManger.registerDriver(driver); //注册mysql驱动 // 方法二:使用反射注册mysql驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.通过DriverManger 驱动管理建立连接 Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mysql001?useUnicode=true&characterEncoding=UTF-8","root", "root"); // 3.获得语句对象(需要SQL语句) String sql = "create table tb_user (`id` int(10) primary key auto_increment,`name` varchar(20),`age` int(10) );"; // 4.connction.createStatement() 创建一个Statement对象 将SQL语句发送到数据库; Statement stm= connection.createStatement(); stm.executeUpdate(sql); // 5.释放资源 stm.close(); connection.close();
四.代码重构. (把获取数据库连接和释放连接封装在方法里)
1. 参数是硬编码的代码中,不推荐使用,不利于代码维护。
/*1. 参数: private static String driverName = "com.mysql.jdbc.Driver"; private static String url = "jdbc:mysql://localhost:3306/mysql001"; private static String userName = "root"; private static String passWorld = "root"; //2. 懒汉模式获取 JDBCUtil类的一个实例 private static JDBCUtil instance = null; private JDBCUtil(){} public static JDBCUtil getInstance(){ if(instance == null){ instance = new JDBCUtil(); } return instance; } //3. 加载驱动 static{ try { Class.forName(driverName); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //4. 获得链接对象 public Connection getConn(){ Connection conn = null; try { return DriverManager.getConnection(url,userName,passWorld); } catch (SQLException e) { e.printStackTrace(); } return conn; } //5. 关闭链接资源 public void close(ResultSet rs, Statement st, Connection conn){ 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(); } } } } */
2.把参数放到资源文件中 jdbc.properties
driverName = com.mysql.jdbc.Driver url = jdbc:mysql://localhost:3306/mysql001 userName = root password = root
public class JDBCUtil { static Properties prop = new Properties(); // 懒汉模式获取 JDBCUtil类的一个实例 private JDBCUtil(){} private static JDBCUtil instance = null; public static JDBCUtil getInstance(){ if(instance == null){ instance = new JDBCUtil(); } return instance; } static{ try { prop.load(new FileInputStream("jdbc.properties")); Class.forName(prop.getProperty("driverName")); } catch (Exception e) { e.printStackTrace(); } } //获得链接对象 public Connection getConnection(){ Connection conn = null; try { conn = DriverManager.getConnection(prop.getProperty("url"),prop.getProperty("userName"),prop.getProperty("password")); } catch (SQLException e) { e.printStackTrace(); } return conn; } //关闭所有链接资源 public void closeAll(ResultSet rs, Statement st, Connection conn){ 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(); } } } } }
五.domain 和 DAO 层认识
分层的目的:解耦,方便维护,责任分离;
1. 先创建一个domain包 ,在包里面创建相应的实体类 (Student)
2. 在domain包的同一级,建一个dao包,这个包里面写接口: ( IStudentDao )
3. 在dao这个包里面再建一个包: impl 包 ,实现dao包里面的接口。(StudentDaoImpl)
下面的代码,没有使用 prepareStatement ,所以不正规,建议别看。
1. com.domain 包 User 实体类
package com.domain; public class User { private String u_name; private int u_age; public String getU_name() { return u_name; } public void setU_name(String u_name) { this.u_name = u_name; } public int getU_age() { return u_age; } public void setU_age(int u_age) { this.u_age = u_age; } public User() { } public User(String u_name, int u_age) { super(); this.u_name = u_name; this.u_age = u_age; } @Override public String toString() { return "User [u_name=" + u_name + ", u_age=" + u_age + "]"; } }
2. com.dao 包下的 IUserDao 接口
package com.dao; import java.util.List; import com.domain.User; public interface IUserDao { void addUserInfo(User user); void deleteUserInfo(User user); void updateUserAgeInfo(User user,int age); User selectUserInfoByName(String name); List<User> selectAllUserInfo(); }
3. com.dao.impl 包 UserDaoImpl 类
package com.dao.impl; import org.junit.Test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import com.Util.JDBCUtil; import com.dao.IUserDao; import com.domain.User; public class UserDaoImpl implements IUserDao{ ResultSet rs = null; Statement st = null; Connection conn = null; User user = null; /*常见的异常错误:No operations allowed after connection closed. 因为 当 数据库的连接Connection是一个Static的,程序共享这一个Connection。 那么第一次对数据库操作没问题,当把Connection关闭后,第二次还想操作数据库时Connection肯定不存在了。 所以为了解决这个问题:我们最好把 创建Connection的实例写到每个操作的方法中。 */ /* 1. 增加用户信息*/ @Test public void addUserInfo(User user){ try { conn = JDBCUtil.getInstance().getConnection(); st = conn.createStatement(); int row = st.executeUpdate("insert into tb_user (id,`name`,age)values(null,\'"+user.getU_name()+"\',"+user.getU_age()+")"); if(row > 0){ System.out.println("添加用户成功"); } } catch (SQLException e) { e.printStackTrace(); } JDBCUtil.getInstance().closeAll(rs,st,conn); } /*2. 删除用户信息*/ @Test public void deleteUserInfo(User user) { try{ conn = JDBCUtil.getInstance().getConnection(); st = conn.createStatement(); int row = st.executeUpdate("delete from tb_user where name = \'"+user.getU_name()+"\'"); if(row > 0){ System.out.println("删除用户成功"); } }catch (Exception e) { e.printStackTrace(); } JDBCUtil.getInstance().closeAll(rs,st,conn); } /*3. 修改用户信息*/ @Test public void updateUserAgeInfo(User user,int age) { try{ conn = JDBCUtil.getInstance().getConnection(); st = conn.createStatement(); int row = st.executeUpdate("update tb_user set age = "+age+" where name = \'"+user.getU_name()+"\'"); if(row > 0){ System.out.println("修改用户信息成功"); } }catch (Exception e) { e.printStackTrace(); } JDBCUtil.getInstance().closeAll(rs,st,conn); } /*4. 通过name查询一个用户信息*/ @Test public User selectUserInfoByName(String name) { try{ //这里如果不写,当方法3中调用次方法时,就会因为 Connection 关闭而报错。 conn = JDBCUtil.getInstance().getConnection(); st = conn.createStatement(); rs = st.executeQuery("select * from tb_user where `name` = \'"+name+"\'"); while(rs.next()){ user = new User(); user.setU_name(rs.getString("name")); user.setU_age(rs.getInt("age")); } }catch (Exception e) { e.printStackTrace(); } JDBCUtil.getInstance().closeAll(rs,st,conn); return user; } /*5. 查询所有用户信息*/ @Override public List<User> selectAllUserInfo() { List<User> userlist = new ArrayList(); try{ conn = JDBCUtil.getInstance().getConnection(); st = conn.createStatement(); rs = st.executeQuery("select * from tb_user"); while(rs.next()){ User user = new User(); user.setU_name(rs.getString("name")); user.setU_age(rs.getInt("age")); userlist.add(user); } }catch (Exception e) { e.printStackTrace(); } JDBCUtil.getInstance().closeAll(rs,st,conn); return userlist; } }
4. com.test 包 TestDao 测试类
package com.test; import java.util.List; import java.util.Scanner; import org.junit.Test; import com.dao.IUserDao; import com.dao.impl.UserDaoImpl; import com.domain.User; public class TestDao { IUserDao userdao = new UserDaoImpl(); @Test public void addUserInfo(){ User user = new User("赵本本",75); //获取数据 userdao.addUserInfo(user); //将数据传到Dao层进行数据库操作 } @Test public void deleteUserInfo(){ User user = new User("赵本本",75); userdao.deleteUserInfo(user);; } /*修改用户信息*/ @Test public void updateUserAgeInfo(){ System.out.println("请输入要修改的用户姓名:"); String name = new Scanner(System.in).nextLine(); //将name传到dao层验证该用户是否存在 User user = userdao.selectUserInfoByName(name); if(user != null){ System.out.println("请输入修改后的用户年龄:"); int age = new Scanner(System.in).nextInt(); //将user对象和age传到dao层进行修改数据操作 userdao.updateUserAgeInfo(user,age);; }else{ System.out.println("不存在该用户,gameover!!"); } } @Test public void selectUserInfoByName(){ System.out.println("请输入查询的学生姓名"); String name = "李四"; System.out.println(userdao.selectUserInfoByName(name)); } @Test public void selectAllUserInfo(){ List<User> userlist = userdao.selectAllUserInfo(); for (User user : userlist) { System.out.println(user); } } }
6. 数据库表:
CREATE TABLE `tb_user` ( `id` int(10) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `age` int(10) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
六 .Statement与PreparedStatement的区别
1. 用PreparedStatement 可以写 动态参数化的查询(带参数的sql查询语句),不需要我们去拼接字符串,比起凌乱的字符串追加似的查询,PreparedStatement查询可读性更好、更安全。
2. PrepareStatement是预处理语句 PreparedStatemnt的速度比Statement更快。
3. PreparedStatement可以防止SQL注入式攻击
- 如果你是做Java web应用开发的,那么必须熟悉那声名狼藉的SQL注入式攻击。在SQL注入攻击里,恶意用户通过SQL元数据绑定输入,比如:某个网站的登录验证SQL查询代码为
strSQL = "SELECT * FROM users WHERE name = \'" + userName + "\' and pw = \'"+ passWord +"\';
恶意填入:
userName = "1\' OR \'1\'=\'1"; passWord = "1\' OR \'1\'=\'1";
那么最终SQL语句变成了:
strSQL = "SELECT * FROM users WHERE name = \'1\' OR \'1\'=\'1\' and pw = \'1\' OR \'1\'=\'1\';
因为WHERE条件恒为真,这就相当于执行:
strSQL = "SELECT * FROM users;
因此可以达到无账号密码亦可登录网站。如果恶意用户要是更坏一点,用户填入
-
:strSQL = "SELECT * FROM users WHERE name = \'any_value\' and pw = \'\'; DROP TABLE users";
-
这样一来,虽然没有登录,但是数据表都被删除了。
防SQL注入代码:
public Student login(String name, String password) { PreparedStatement ps = null; Connection conn = null; try { conn = JDBCUtil.getInstance().getConnection(); String sql = "select * from student where username = ? and password = ?"; ps = conn.prepareStatement(sql); ps.setObject(1, name); ps.setObject(2, password); rs = ps.executeQuery(); while(rs.next()){ stu = new Student(); stu.setUserName(rs.getString("username")); stu.setPassWord(rs.getString("password")); stu.setAge(rs.getInt("age")); stu.setSex(rs.getBoolean("sex")); } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtil.getInstance().closeAll(rs,ps,conn); } return stu; }
@Test public void login1(){ System.out.println("请输入用户名"); String name = new Scanner(System.in).nextLine(); System.out.println("请输入密码"); String password = new Scanner(System.in).nextLine(); // 这种方式 如果密码和用户名只要其中一个 使用 \' or 1=1 or \' 都能登录成功。 Student stu = studao.login(name, password); if(stu!=null){ System.out.println("登录成功"); }else{ System.out.println("登录失败"); } }
7.登录的三种方式
.登录的正规方式: 先判断用户名,再判断密码。
@Override public Student login(String name) { try { conn = JDBCUtil.getInstance().getConnection(); String sql = "select * from student where username = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, name); rs = ps.executeQuery(); while(rs.next()){ stu = new Student(); stu.setUserName(rs.getString("username")); stu.setPassWord(rs.getString("password")); stu.setAge(rs.getInt("age")); stu.setSex(rs.getBoolean("sex")); } } catch (Exception e) { }finally { JDBCUtil.getInstance().closeAll(rs,st,conn); } return stu; }
参考链接:https://www.cnblogs.com/shanheyongmu/p/5897176.html
https://blog.csdn.net/github_39430101/article/details/77844465
以上是关于jdbc 编程的主要内容,如果未能解决你的问题,请参考以下文章