一、JDBC规范
二、开发JDBC程序
DriverManager. registerDriver(Driver driver) ;
Class.forName("com.mysql.jdbc.Driver");
1 public void test1() throws Exception { 2 // 加载驱动,反射 3 Class.forName("com.mysql.jdbc.Driver"); 4 5 // 获取连接Connection,用java.sql.Connection sun的接口,多态,能用接口就不用类,导java.sql包 6 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "GUOJIE123"); 7 8 // 得到执行sql语句的对象Statement,全是接口,实现在jar包中 9 Statement createStatement = connection.createStatement(); 10 11 // 执行sql语句返回结果 12 ResultSet executeQuery = createStatement.executeQuery("select * from emp"); 13 14 // 处理结果 15 while(executeQuery.next()) { // 开始指向表头,一次移动一行 16 System.out.println(executeQuery.getObject(1)); // 数据库都是从1开始,每次取一列 17 System.out.println(executeQuery.getObject(2)); 18 System.out.println(executeQuery.getObject(3)); 19 } 20 21 // 关闭资源 22 executeQuery.close(); 23 createStatement.close(); 24 connection.close(); 25 } 26 }
三、JDBC常用的类和接口
1、java.sql.Drivermanager类
创建连接
getConnection(String url, String user, String password) // 试图建立到给定数据库 URL 的连接
getConnection("jdbc:mysql://localhost:3306/day06", "root", "123");
URL:SUN公司与数据库厂商之间的一种协议。
jdbc:mysql://localhost:3306/db1 协议 子协议 IP : 端口号 数据库
mysql: jdbc:mysql://localhost:3306/day14 或者 jdbc:mysql:///day14(默认本机连接) oracle: jdbc:oracle:thin:@localhost:1521:sid // thin瘦的,
getConnection(String url, Properties info);
1 Properties info = new Properties();//要参考数据库文档 2 info.setProperty("user", "root"); 3 info.setProperty("password","root");
getConnection(String url);
DriverManager.getConnection("jdbc:mysql://localhost:3306/day14?user=root&password=root");
JUnit测试类
import org.junit.Test;
注意:测试方法不能有返回值和参数
2、java.sql.Connection接口
一个连接
接口的实现在数据库驱动中,所有与数据库交互都是基于连接对象的。
Statement createStatement(); //创建操作sql语句的对象
3、java.sql.Statement接口
操作sql语句,并返回相应结果的对象(车)
ResultSet executeQuery(String sql) //根据查询语句返回结果集,只能执行select语句。
int executeUpdate(String sql) // 根据执行的DML(insert update delete)语句,返回受影响的行数。
boolean execute(String sql) // 此方法可以执行任意sql语句。返回boolean值,表示是否返回ResultSet结果集,仅当执行select语句,且有返回结果集时返回true, 其它语句都返回false
4、java.sql.ResultSet接口
1 byte tityint 2 short smallint 3 int int 4 long bigint 5 float float 6 double double 7 String char varchar 8 Date date
Clob 数据库中大文本
1 boolean next() // 将光标从当前位置向下移动一行 2 int getInt(int colIndex) // 以int形式获取ResultSet结果集当前行指定列号值 3 int getInt(String colLabel) // 以int形式获取ResultSet结果集当前行指定列名值 4 float getFloat(int colIndex) // 以float形式获取ResultSet结果集当前行指定列号值 5 float getFloat(String colLabel) // 以float形式获取ResultSet结果集当前行指定列名值 6 String getString(int colIndex) // 以String 形式获取ResultSet结果集当前行指定列号值 7 String getString(String colLabel) // 以String形式获取ResultSet结果集当前行指定列名值 8 Date getDate(int columnIndex); 9 Date getDate(String columnName); 10 void close() // 关闭ResultSet 对象
使用JDBC实现CRUD操作
1 // 实体类,对应数据库的每一行 2 public class Emp { 3 private int id; 4 private String name; 5 private String gender; 6 7 public int getId() { 8 return id; 9 } 10 public void setId(int id) { 11 this.id = id; 12 } 13 public String getName() { 14 return name; 15 } 16 17 public void setName(String name) { 18 this.name = name; 19 } 20 public String getGender() { 21 return gender; 22 } 23 public void setGender(String gender) { 24 this.gender = gender; 25 } 26 27 @Override 28 public String toString() { 29 return "Emp [id=" + id + ", name=" + name + ", gender=" + gender + "]"; 30 } 31 }
1 public class TestCRUD { 2 3 @Test 4 public void test() throws Exception { 5 // 加载驱动,反射 6 Class.forName("com.mysql.jdbc.Driver"); 7 8 // 获取连接Connection,用java.sql.Connection sun的接口,多态,能用接口就不用类,导java.sql包 9 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "GUOJIE123"); 10 11 // 得到执行sql语句的对象Statement,全是接口,实现在jar包中 12 Statement createStatement = connection.createStatement(); 13 14 // 执行sql语句返回结果 15 ResultSet executeQuery = createStatement.executeQuery("select * from emp"); 16 List<Emp> list = new ArrayList<Emp>(); 17 18 // 处理结果 19 while(executeQuery.next()) { // 开始指向表头,一次移动一行 20 Emp emp = new Emp(); // 要是放在外面的话,会造成覆盖显示最后一行数据,因list中存放的是emp的引用,内存地址都一样,以最后修改的一次为结果 21 emp.setId(executeQuery.getInt("id")); 22 emp.setName(executeQuery.getString("name")); 23 emp.setGender(executeQuery.getString("gender")); 24 list.add(emp); 25 } 26 27 // 关闭资源 28 executeQuery.close(); 29 createStatement.close(); 30 connection.close(); 31 32 for (Emp emp : list) { 33 System.out.println(emp); 34 } 35 } 36 }
1 @Test 2 public void test2() throws Exception { 3 // 获取连接Connection 4 Connection connection = null; 5 // 得到执行sql语句的对象Statement 6 Statement createStatement = null; 7 // 执行sql语句返回结果 8 ResultSet executeQuery = null; 9 try { 10 // 加载驱动,反射 11 Class.forName("com.mysql.jdbc.Driver"); 12 13 connection = DriverManager.getConnection( 14 "jdbc:mysql://localhost:3306/db1", "root", "GUOJIE123"); 15 16 createStatement = connection.createStatement(); 17 18 executeQuery = createStatement.executeQuery("select * from emp"); 19 20 // 处理结果 21 while (executeQuery.next()) { // 开始指向表头,一次移动一行 22 System.out.println(executeQuery.getObject(1)); // 数据库都是从1开始,每次取一列 23 System.out.println(executeQuery.getObject(2)); 24 System.out.println(executeQuery.getObject(3)); 25 } 26 } catch (Exception e) { 27 // TODO Auto-generated catch block 28 e.printStackTrace(); 29 } finally { 30 // 关闭资源 31 if (executeQuery != null) { 32 try { 33 executeQuery.close(); // executeQuery中还可能出错,继续try-catch 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 executeQuery = null; // 让资源回收器回收 38 } 39 40 if (createStatement != null) { 41 try { 42 createStatement.close(); 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } 46 createStatement = null; // 让资源回收器回收 47 } 48 49 if (connection != null) { 50 try { 51 connection.close(); 52 } catch (Exception e) { 53 e.printStackTrace(); 54 } 55 connection = null; // 让资源回收器回收 56 } 57 } 58 59 }
SQL注入:
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击
原理:
1.在输入时连接一个永远为真的一个值
2.使用mysql 中的 – 注释
解决SQL注入:使用PreparedStatement 取代 Statement,对sql语句进行预编译
PreparedStatement pst=con.prepareStatement(String sql);
PreparedStatement 解决SQL注入原理:运行在SQL中参数以?占位符的方式表示
select * from user where username = ? and password = ? ;
将带有?的SQL 发送给数据库完成编译 (不能执行的SQL 带有?的SQL 进行编译 叫做预编译),在SQL编译后发现缺少两个参数
PreparedStatement 可以将? 代替参数 发送给数据库服务器,因为SQL已经编译过,参数中特殊字符不会当做特殊字符编译,无法达到SQL注入的目的
四、事务
事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功
1 start transaction // 开启事务 2 Rollback // 回滚事务,将数据恢复到事务开始时状态 3 Commit // 提交事务,对事务中进行操作,进行确认操作,事务在提交后,数据就不可恢复
2、mysql管理事务
方式一 :同时事务管理SQL 语句
同上开始事务命令
方式二:数据库中存在一个自动提交变量 ,通过 show variables like '%commit%'; ---- autocommint 值是 on,说明开启自动提交
关闭自动提交 set autocommit = off / set autocommit = 0
如果设置autocommit 为 off,意味着以后每条SQL 都会处于一个事务中,相当于每条SQL执行前 都执行 start transaction
1 Connection.setAutoCommit(false); // 相当于start transaction 2 Connection.rollback(); rollback 3 Connection.commit(); commit
4、事物特性 ACID
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
(2)一致性(Consistency)
事务前后数据的完整性必须保持一致。
(3)隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
(4)持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
如果不考虑隔离性,可能会引发如下问题:
(1)脏读 :指一个事务读取另一个事务未提交的数据
(2)不可重复读:在一个事务先后两次读取发生数据不一致情况,第二次读取到另一个事务已经提交数据 (强调数据更新 update)
(3)虚读(幻读) :在一个事务中,第二次读取发生数据记录数的不同 ,读取到另一个事务已经提交数据 (强调数据记录变化 insert )
(4)丢失更新 :两个事务对同一条记录进行操作,后提交的事务,将先提交的事务的修改覆盖了
解决:数据库内部定义了四种隔离级别,用于解决三种隔离问题
1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
3 Read committed:可避免脏读情况发生(读已提交)
4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
mysql中默认的事务隔离级别是 Repeatable read.
oracle 中默认的事务隔离级别是 Read committed
操作数据库内部隔离级别
set session transaction isolation level 设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
JDBC中指定事务的隔离级别
Connection接口中定义事务隔离级别四个常量:
(1)static int TRANSACTION_READ_COMMITTED
指示不可以发生脏读的常量;不可重复读和虚读可以发生。
(2)static int TRANSACTION_READ_UNCOMMITTED
指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
(3)static int TRANSACTION_REPEATABLE_READ
指示不可以发生脏读和不可重复读的常量;虚读可以发生。
(4)static int TRANSACTION_SERIALIZABLE
指示不可以发生脏读、不可重复读和虚读的常量。
通过 void setTransactionIsolation(int level) 设置数据库隔离级别
6、事务隔离级别总结
read uncommitted 什么问题也解决不了.
read committed 可以解决脏读,其它解决不了.
Repeatable read 可以解决脏读,可以解决不可重复读,不能解决虚读.
Serializable 它会锁表,可以解决所有问题.
安全性:serializable > repeatable read > read committed > read uncommitted
性能 :serializable < repeatable read < read committed < read uncommitted
结论: 实际开发中,通常不会选择 serializable 和 read uncommitted
mysql默认隔离级别 repeatable read ,oracle默认隔离级别 read committed
ThreadLocal可以理解成是一个Map集合
Map<Thread,Object>
set方法是向ThreadLocal中存储数据,那么当前的key值就是当前线程对象.
get方法是从ThreadLocal中获取数据,它是根据当前线程对象来获取值。
如果我们是在同一个线程中,只要在任意的一个位置存储了数据,在其它位置上,就可以获取到这个数据
关于JdbcUtils中使用ThreadLocal
1.声明一个ThreadLocal
private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
2.在getConnection()方法中操作
1 Connection con = tl.get(); 直接从ThreadLocal中获取,第一次返回的是null. 2 if (con == null) { 3 // 2.获取连接 4 con = DriverManager.getConnection(URL, USERNAME, PASSWORD); 5 tl.set(con); //将con装入到ThreadLocal中。 6 }
多个事务对同一条记录进行了操作,后提交的事务将先提交的事务操作覆盖了
1.悲观锁
悲观锁 (假设丢失更新一定会发生 )
利用数据库内部锁机制,管理事务
提供的锁机制
(1)共享锁
select * from table lock in share mode(读锁、共享锁)
(2)排它锁
select * from table for update (写锁、排它锁)
update语句默认添加排它锁
2.乐观锁
乐观锁 (假设丢失更新不会发生)
采用程序中添加版本字段解决丢失更新问题
create table product ( id int, name varchar(20), updatetime timestamp ); insert into product values(1,'冰箱',null); update product set name='洗衣机' where id = 1;
解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段id,
与修改时版本字段不一致,说明别人进行修改过数据 (重改)
五、连接池
就是创建一个容器,用于装入多个Connection对象,在使用连接对象时,从容器中获取一个Connection,
使用完成后,在将这个Connection重新装入到容器中。这个容器就是连接池。(DataSource),也叫做数据源.
我们可以通过连接池获取连接对象.
优点:节省创建连接与释放连接 性能消耗(连接池中连接起到复用的作用 ,提高程序性能)
1、自定义连接池
(1)创建一个MyDataSource类,在这个类中创建一个LinkedList<Connection>
(2)在其构造方法中初始化List集合,并向其中装入5个Connection对象。
(3)创建一个public Connection getConnection();从List集合中获取一个连接对象返回.
(4)创建一个 public void readd(Connection) 这个方法是将使用完成后的Connection对象重新装入到List集合中.
代码问题:
1.连接池的创建是有标准的.
在javax.sql包下定义了一个接口 DataSource
简单说,所有的连接池必须实现javax.sql.DataSource接口,我们的自定义连接池必须实现DataSource接口。
2.我们操作时,要使用标准,怎样可以让 con.close()它不是销毁,而是将其重新装入到连接池.
要解决这个问题,其本质就是将Connection中的close()方法的行为改变。
怎样可以改变一个方法的行为(对方法功能进行增强)
1.继承
2.装饰模式
1.装饰类与被装饰类要实现同一个接口或继承同一个父类
2.在装饰类中持有一个被装饰类引用
3.对方法进行功能增强。
3.动态代理
可以对行为增强
Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);
结论:
Connection对象如果是从连接池中获取到的,那么它的close方法的行为已经改变了,不在是销毁,而是重新装入到连接池。
1.连接池必须实现javax.sql.DataSource接口。
2.要通过连接池获取连接对象 DataSource接口中有一个 getConnection方法.
3.将Connection重新装入到连接池 使用Connection的close()方法。
DBCP是apache的一个开源连接池。
要想使用DBCP连接池,要下载jar包
导入时要导入两个
commons-dbcp-1.4.jar
commons-pool-1.5.6.jar
关于dbcp连接池使用
1.手动配置(手动编码)
BasicDataSource bds = new BasicDataSource(); // 需要设置连接数据库最基本四个条件 bds.setDriverClassName("com.mysql.jdbc.Driver"); bds.setUrl("jdbc:mysql:///day18"); bds.setUsername("root"); bds.setPassword("abc"); // 得到一个Connection Connection con = bds.getConnection();
2.自动配置(使用配置文件)
1 Properties props = new Properties(); 2 FileInputStream fis = new FileInputStream("D:\java1110\workspace\day18_2\src\dbcp.properties"); 3 props.load(fis); 4 5 DataSource ds = BasicDataSourceFactory.createDataSource(props);
(2)C3P0(必会)
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。
目前使用它的开源项目有Hibernate,Spring等。
c3p0与dbcp区别:
c3p0有自动回收空闲连接功能,DBCP没有
c3p0连接池使用
1.导包
c3p0-0.9.1.2.jar
使用
1.手动
1 ComboPooledDataSource cpds = new ComboPooledDataSource(); 2 cpds.setDriverClass("com.mysql.jdbc.Driver"); 3 cpds.setJdbcUrl("jdbc:mysql:///day18"); 4 cpds.setUser("root"); 5 cpds.setPassword("abc");
2.自动(使用配置文件)
c3p0的配置文件可以是properties也可以是xml.
c3p0的配置文件如果名称叫做 c3p0.properties or c3p0-config.xml 并且放置在classpath路径下(对于web应用就是classes目录)
那么c3p0会自动查找。
注意:我们其时只需要将配置文件放置在src下就可以。
使用:
ComboPooledDataSource cpds = new ComboPooledDataSource(); // 它会在指定的目录下查找指定名称的配置文件,并将其中内容加载。
(3)Tomcat内置连接池
Tomcat管理连接池
<Context> <Resource name="jdbc/EmployeeDB" auth="Container" type="javax.sql.DataSource" username="root" password="abc" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day18" maxActive="8" maxIdle="4"/> </Context>
context.xml文件位置
1.在Tomcat/conf/context.xml 这时这个连接池是给整个服务器使用的。
2.在Tomcat/conf/Catalina/localhost 这时这个连接池只给localhost虚拟主机使用。
3.将context.xml文件放置在web应用的META-INF下,只给本项目使用
注意:如果是全局设置,那么我们需要将数据库驱动放置在tomcat/lib目录下
从tomcat中获取连接池
我们在servlet中获取连接池对象,用JNDI技术实现
1 Context context = new InitialContext(); 2 Context envCtx = (Context)context.lookup("java:comp/env"); // 固定路径 3 DataSource datasource = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,
JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务
和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是一种命名服务,
在这种服务里,对象不但有名称,还有属性。
六、元数据
1 getURL() // 返回一个String类对象,代表数据库的URL。 2 getUserName() // 返回连接当前数据库管理系统的用户名。 3 getDriverName() // 返回驱动驱动程序的名称。 4 getPrimaryKeys(String catalog, String schema, String table) // 返回指定表主键的相关描述 结果集
每个主键列描述都有以下列:
TABLE_CAT String => 表类别(可为 null)
TABLE_SCHEM String => 表模式(可为 null)
TABLE_NAME String => 表名称
COLUMN_NAME String => 列名称
KEY_SEQ short => 主键中的序列号(值 1 表示主键中的第一列,值 2 表示主键中的第二列)。
PK_NAME String => 主键的名称(可为 null)
1 getTables 2 getColumns 3 getPrimaryKeys
PreparedStatement . getParameterMetaData() // 获得代表PreparedStatement元数据的ParameterMetaData对象
Select * from user where name=? And password=?
ParameterMetaData对象
1 getParameterCount() // 获得指定参数的个数 2 getParameterTypeName(int param) // 获得指定参数的sql类型
在获取参数类型时会产生异常
java.sql.SQLException: Parameter metadata not available for the given statement
解决方案:
在url后添加参数 jdbc:mysql:///day18?generateSimpleParameterMetadata=true
添加这个参数后,我们在获取,它的结果也是varchar,
原因:是mysql驱动的支持问题,Oracle可以
ResultSet. getMetaData() // 获得代表ResultSet对象元数据的ResultSetMetaData对象
ResultSetMetaData对象
1 getColumnCount() // 返回resultset对象的列数 2 getColumnName(int column) // 获得指定列的名称 3 getColumnTypeName(int column) // 获得指定列的类型
七、DbUtils
1.QueryRunner类
它是用于执行sql语句的类。
(1)query 用于执行select
(2)update 用于执行update delete insert
(3)batch 批处理
2.ResultSetHandler接口
用于定义结果集的封装
它提供九个实现类,可以进行不同的封装。
3.DbUtils类
它提供关于关闭资源以及事务rollback,commit操作
2、QueryRunner
单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
(1)获取QueryRunner
new QueryRunner() // 如果是使用这种构造创建的QueryRunner,它的事务是手动控制. new QueryRunner(DataSource ds); // 如果是使用这种构造,它的事务是自动事务,简单说,一条sql一个事务。
(2)QueryRunner中的三个核心方法
query
update
batch
对于上述三个方法,它们提供很多重载。
如果QueryRunner在创建时,没有传递DataSource参数,那么在使用
query,update,batch方法时,要传递Connection参数
如果QueryRunner在创建时,传递了Dataource参数,好么在使用
query,update,batch方法时,不需要传递Connection参数。
1 QueryRunner runner=new QueryRunner(); 2 runner.query(Connection,sql,ResultSetHandler,Object... param); 3 runner.update(Connection,sql,Object...param); 4 runner.batch(Connection con,sql,Object[][] objs); 5 6 QueryRunner runner=new QueryRunner(DataSource ds); 7 runner.query(sql,ResultSetHandler,Object... param); 8 runner.update(sql,Object...param); 9 runner.batch(sql,Object[][] objs);
3、DbUtils类
提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的
public static void close(…) throws java.sql.SQLException // DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。 public static void closeQuietly(…) // 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLException。 public static void commitAndCloseQuietly(Connection conn) // 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。 public static boolean loadDriver(java.lang.String driverClassName) //这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
模仿QueryRunner
1 1.query方法模仿 2 public <T> T query(Connection con, String sql, MyResultSetHandler<T> mrs,Object... params) throws SQLException { 3 4 PreparedStatement pst = con.prepareStatement(sql); // 得到一个预处理的Statement. 5 // 问题:sql语句中可能存在参数,需要对参数赋值。 6 7 ParameterMetaData pmd = pst.getParameterMetaData(); 8 // 可以得到有几个参数 9 int count = pmd.getParameterCount(); 10 for (int i = 1; i <= count; i++) { 11 pst.setObject(i, params[i - 1]); 12 } 13 14 ResultSet rs = pst.executeQuery(); // 得到了结果集,要将结果集封装成用户想要的对象,但是,工具不可能知道用户需求。 15 16 return mrs.handle(rs); 17 } 18 2.update方法模仿 19 public int update(Connection con, String sql, Object... params) throws SQLException { 20 21 PreparedStatement pst = con.prepareStatement(sql); // 得到一个预处理的Statement. 22 // 问题:sql语句中可能存在参数,需要对参数赋值。 23 24 ParameterMetaData pmd = pst.getParameterMetaData(); 25 // 可以得到有几个参数 26 int count = pmd.getParameterCount(); 27 for (int i = 1; i <= count; i++) { 28 pst.setObject(i, params[i - 1]); 29 } 30 31 int row = pst.executeUpdate(); 32 // 关闭资源 33 pst.close(); 34 return row; 35 }
4、ResultSetHandler接口
1 // 使用BeanUtils实现 2 Object obj = null; 3 4 Map<String, String[]> map = new HashMap<String, String[]>(); 5 6 ResultSetMetaData md = rs.getMetaData(); 7 int count = md.getColumnCount(); 8 9 if (rs.next()) { 10 try { 11 obj = clazz.newInstance(); 12 for (int i = 1; i <= count; i++) { 13 map.put(md.getColumnName(i), 14 new String[] { rs.getString(md.getColumnName(i)) }); 15 } 16 BeanUtils.populate(obj, map); 17 } catch (InstantiationException e) { 18 e.printStackTrace(); 19 } catch (IllegalAccessException e) { 20 e.printStackTrace(); 21 } catch (InvocationTargetException e) { 22 e.printStackTrace(); 23 } 24 25 } 26 27 return obj;
客户信息的CURD操作
web 表现层、service 业务层、dao 持久层、utils 工具包、domain 实体类(javaBean)
// 查询所有客户信息
1 // 1.在success.jsp页面添加连接 2 <a href="${pageContext.request.contextPath}/findAll">查看所有客户信息</a> 3 // 2.在CustomerFindAllServlet中调用service,在service中调用dao,最后得到一个List<Customer> 4 // 3.在showCustomer.jsp页面展示客户信息 5 <c:forEach items="${cs}" var="c"> 6 <tr> 7 <td><input type="checkbox"> 8 </td> 9 <td>${c.id }</td> 10 <td>${c.name}</td> 11 <td>${c.gender }</td> 12 <td>${c.birthday }</td> 13 <td>${c.cellphone }</td> 14 <td>${c.email }</td> 15 <td>${c.preference }</td> 16 <td>${c.type }</td> 17 <td>${c.description }</td> 18 <td><a>编辑</a> <a>删除</a></td> 19 </tr> 20 </c:forEach>
1 // 删除操作 2 // 1.在showCustomer.jsp页面的删除连接上添加参数 客户的id
<a href="${pageContext.request.contextPath}/delByid?id=${c.id}">删除</a> 3 // 2.创建一个CustomerDelByIdServlet,获取请求参数,调用service中删除方法.
删除完成后,需要重新跳转到查询所有的servlet中,再重新查询数据
response.sendRedirect(request.getContextPath()+"/findAll");
1 // 编辑 2 // 1.查询,做回显示 3 <a href="${pageContext.request.contextPath}/findById?id=${c.id}">编辑</a> 4 // 1.创建CustomerFindByIdServlet,得到要查询的id,调用service,得到Custonmer对象。 5 // 2.将customer对象存储到request域,请求转发到customerInfo.jsp页面。 6 // 3.在customerInfo.jsp页面展示客户信息 7 // 注意:客户的id不能修改,所以使用<input type="hidden"> 8 9 // 2.修改 10 // 1.注意使用BeanUtils时的类型转换问题 11 // 2.注意编码问题 12 // post:request.setCharacterEncoding("utf-8"); 13 // get:手动转换 new String(request.getParameter(name).getBytes("iso8859-1"),"utf-8"); 14 15 // 3.进行修改操作 16 String sql = "update customer set name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=? where id=?"; 17 // 修改完成后,在重新查询一次 18 response.sendRedirect(request.getContextPath() + "/findAll");
解决关于回显示时的问题
性别 应该使用radio
1 // 使用自定义标签 2 // 1.定义标签类 extends SimpleTagSupport 3 // 2.定义tld文件 4 <tag> 5 <name>sex</name><!-- 标签名称 --> 6 <tag-class>cn.itcast.customer.tag.GenderTag</tag-class><!-- 标签类 --> 7 <body-content>empty</body-content><!-- 标签体中内容 --> 8 9 <attribute> 10 <name>gender</name> <!-- 属性名称 --> 11 <required>true</required> <!-- 属性必须有 --> 12 <rtexprvalue>true</rtexprvalue><!-- 属性值可以接收el表达式 --> 13 </attribute> 14 </tag> 15 // 3.在页面上使用 16 // 1.使用taglib导入 17 // 2.使用 18 <my:sex gender="${c.gender}" />
使用虚拟主机可以将项目部署成顶级域名
1.在service.xml文件
1.端口修改为80
2. 配置主机
1 <Host name="www.customer.com" appBase="D:java1110workspaceday19_2" 2 unpackWARs="true" autoDeploy="true"> 3 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" 4 prefix="localhost_access_log." suffix=".txt" 5 pattern="%h %l %u %t "%r" %s %b" /> 6 <Context path="" docBase="D:java1110workspaceday19_2WebRoot" /> 7 </Host>
3.在hosts文件中配置