• 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访问数据库的工作过程:  贾琏欲执事

    1. 加载驱动,建立连接
    2. 创建语句对象
    3. 执行SQL语句
    4. 处理结果集
    5. 关闭连接
    //        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注入式攻击

      1. 如果你是做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;

        因此可以达到无账号密码亦可登录网站。如果恶意用户要是更坏一点,用户填入 

      2. :strSQL = "SELECT * FROM users WHERE name = 'any_value' and pw = ''; DROP TABLE users";
      3. 这样一来,虽然没有登录,但是数据表都被删除了。

     防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

  • 相关阅读:
    Python学习之路(一)之Python基础1
    【第十一课】Tomcat原理解析【转】
    【第十课】Tomcat入门
    【第九课】MriaDB密码重置和慢查询日志
    【第八课】php-fpm.conf配置文件解析
    【第七课】Nginx反向代理和负载均衡
    【第六课】Nginx常用配置下详解
    maven项目中更新了核心库后导致一些包提示未定义,如:The import org.json cannot be resolved
    js代码突然在花括号回车自动多加了一个大括号
    SQL0419N 十进制除法运算无效,因为结果将有一个负小数位。 SQLSTATE=42911
  • 原文地址:https://www.cnblogs.com/gshao/p/10236703.html
Copyright © 2020-2023  润新知