数据库实现了数据持久化,但是我们最终还是要在程序中处理数据,那么java代码如何访问数据库读写数据呢?这就需要sun制定的一套数据库标准,就是JDBC(JavaDatabaseConnectivity)。但这只是一个规范,并不是具体的实现。所以数据库厂商按照JDBC标准实现了自己的驱动。如:mysql驱动com.mysql.cj.jdbc.Driver,Oracle驱动oracle.jdbc.OracleDriver。有了这个方案,java就可以访问数据库中的数据了。publicinterfaceConnectionextendsWrapper,AutoCloseable{}publicinterfaceStatementextendsWrapper,AutoCloseable{}publicinterfacePreparedStatementextendsStatement{}publicinterfaceCallableStatementextendsPreparedStatement{}publicinterfaceResultSetextendsWrapper,AutoCloseable{}Java最提倡面向接口开发的经典接口设计是JDBC数据库接口。有问题可以看视频:https://www.bilibili.com/video...连接链接、Statement语句、PreparedStatement准备语句、CallableStatement存储过程、ResultSet结果集。共有三种调用方式:Statement语句、PreparedStatement准备语句和CallableStatement存储过程。推荐使用第二个PreparedStatement来防止SQL注入,预编译性能也很高。使用步骤导入jar包(丰富的工具类)通过程序获取到数据库的连接(用户名,密码)通过程序执行SQL处理结果idea创建项目并导入jar包创建stage2Java项目创建lib目录,将驱动objbc6-11.1.0.7.0复制到lib目录下的项目中,引用此外部jar包入口案例包cn.tedu.jdbc;导入java.sql.*;//测试jdbc//需求:查询cgb2104库中students表的所有数据publicclassTest1{publicstaticvoidmain(String[]args)throwsException{//1、注册驱动Class.forName("com.mysql.jdbc.Driver");//2、获取与数据库的连接//Stringurl="jdbc:mysql://localhost:3306/cgb2104?characterEncoding=utf8";//指定连接哪个数据库Stringurl="jdbc:mysql:///cgb2104?characterEncoding=utf8";//指定连接哪个数据库DatabaseStringuser="root";//使用的用户名Stringpwd="root";//连接使用的密码conn=DriverManager.getConnection(url,user,pwd);//3、获取发送器并执行SQL语句st=conn.createStatement();//4、执行SQLResultSetrs=st.executeQuery("select*fromstudents");//5、解析结果集while(rs.next()){//next()判断结果集中是否有数据for(inti=1;i<=5;i++){//获取每一列的值并打印System.out.println(rs.getString(i));}}//6、释放资源rs.close();//关闭结果集st.close();//关闭发送器conn.close();//关闭连接}}SQL注入/*准备user2表(id/name/password)并准备数据CREATETABLE`user2`(`id`int(11)PRIMARYKEYauto_increment,`name`varchar(10)defaultNULL,`password`varchar(10)defaultNULL);*//需求:使用jdbc根据用户名密码查询cgb2104库中User表//SQL注入攻击问题privatestaticvoidlogin(){try{Class.forName("com.mysql.jdbc.Driver");Stringurl="jdbc:mysql:///cgb2104?characterEncoding=utf8";Connectionconn=DriverManager.getConnection(url,"root","root");Statementst=conn.createStatement();//Stringsql="select*fromuser2wherename='jack'andpassword='123456'";//死写Stringuser=newScanner(System.in).nextLine();//用户输入插孔'#Stringpwd=newScanner(System.in).nextLine();//SQL注入攻击问题:本质上是因为SQL语句中出现了特殊符号#,改变了SQL语义Stringsql="select*fromuser2wherename='"+user+"'andpassword='"+pwd+"'";ResultSetrs=st.executeQuery(sql);//执行查询SQL并返回结果集if(rs.next()){System.out.println("登录成功~");}else{System.out.println("登录失败~");}st.close();conn.close();}catch(Exceptione){e.printStackTrace();//如果有异常,直接打印异常信息//System.out.println("执行失败..");//在线}}SQL注入解决方案//SQL注入攻击解决方案privatestaticvoidlogin2(){try{Class.forName("com.mysql.jdbc.Driver");Stringurl="jdbc:mysql:///cgb2104?characterEncoding=utf8";Connectionconn=DriverManager.getConnection(url,"root","root");//Statementst=conn.createStatement();不安全,会是SQLAttackStringuser=newScanner(System.in).nextLine();//用户输入jack'#Stringpwd=newScanner(System.in).nextLine();//?调用占位符,SQL骨架Stringsql="选择*fromuser2wherename=?andpassword=?";//首先将SQLskeleton发送到数据库执行PreparedStatementps=conn.prepareStatement(sql);//设置SQL中?的参数ps.setString(1,user);//Setvalueforfirst?isuserps.setString(2,pwd);//Setvalueforsecond?ispwdResultSetrs=ps.executeQuery();//执行拼接SQL,返回结果集if(rs.next()){System.out.println("登录成功~");}else{System.out.println("登录失败~");}ps.close();conn.close();}catch(Exceptione){e.printStackTrace();//如果有异常,直接打印异常信息//System.out.println("Executionfailed..");//在线}}JDBC常见问题Class.forName这句话有用吗?Class.forName可以指定类路径动态创建对象实例,但是JDBC语句不返回对象,那么写这句话有什么作用呢?看java.sql.Driver.class的源码才知道真相。事实证明,它使用静态代码块来创建对象。静态{尝试{DriverManager.registerDriver(newDriver());}catch(SQLExceptionvar1){thrownewRuntimeException("无法注册驱动程序!");}}写完创造了,为什么不写呢?不写怎么能执行呢?Java提供了SPI机制,用户可以自行配置类。更高版本的JDBC驱动程序已经引入了这种支持。如果用户使用Class.forName方法指定驱动,如果不写这句话,Java会自动在META-INF/services/java.sql.Driver文件中寻找启动类。驱动版本不同版本的mysql需要不同版本的驱动Mysql5.0xmysql-connector-java-5.1.32.jarMysql8.0xmysql-connector-java-8.0.21.jarDriver变成:com.mysql.cj.jdbc。驱动,如果中间cjurl太多,必须加时区参数:serverTimezone=Asia/Shanghai中文乱码url添加参数:characterEncoding=utf8防止中文乱码Stringurl="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false";SQL注入Stringcondition="ChenQiang";Stringcondition="ChenQiang'or1=1or'";Stringcondition="ChenQiang'ortrueor'";Stringsql="select*fromteacherswheretname='"+condition+"'";在sql中使用'单撇号作为字符串的结束符,或者只要满足一个条件,另一个就不用判断了,而sql查询因恶意无效,应该只显示一条数据,所有结果都会显示出来。注入后形成的SQL:SELECT*FROMteachersWHEREtname='ChenQiang'OR1=1OR''大家想象一下,如果是财务表,你只能看到自己的信息,却看到了大家的信息。结果新员工工资比你高,你说烦。PreparedStatement语句SQL注入解决方案:Statement对象替换为PreparedStatement对象sql="select*fromteacherswheretname=?";#参数使用问号PreparedStatementstat=cn.prepareStatement(sql);#对象替换stat.setString(1,condition);#对应参数类型,问号个数ResultSetrs=stat.executeQuery();#去掉sql参数后的结果PS:SELECT*FROMteachersWHEREtname='ChenQiang\'or1=1or\''使用Escape字符,屏蔽SQL中的恶意字符。既解决了sql注入问题,又让系统更加安全。PreparedStatement还有一个很大的优势。它是一个预编译语句。它的主要部分mysql是预编译和缓存的。下次这部分就不用解析了,只需要把条件拼进去,这样执行效率比每次都编译sql语句的语句高很多。常见错误java.lang.ClassNotFoundException:com.mysql.jdbc.Driver错误原因:1)jar没有导入,没有builder路径2)Class.forName("com.mysql.jdbc.Driver");stringtypoUnknowndatabasemydb;错误原因:数据库名称拼写错误Accessdeniedforuser'root123'@'localhost'(usingpassword:YES)错误原因:数据库用户名或密码错误Table'py-school-db.mydb'doesn'texist错误原因:表不存在,或者表名有误
