jdbc从入门到放弃、xml、测试、注解

洪祺
2023-12-01

JDBC从入门到放弃

jdbc:

jdbc的基础:

准备工作:

1.导入数据库驱动的架包(.jar文件)

2.要对架包进行buildPath操作

3.建立代码实现的数据库的访问

java database connectivity SuN公司

创建连接
三个参数的

	 第一个参数:数据库的访问路径
	 第二个参数:用户名
	 第三个参数:密码

jdbc 工作原理

Connection Statement ResultSet

最原始的jdbc连接方式:

对于1.创建数据库连接的驱动(干活儿的对象)
DriverManager.registerDriver(new com.mysql.jdbc.Driver());

暂时理解像是一个声明,在这里使用的是mysql的驱动,jvm调用mysql的功能工作

public class Demo1 {
	public static void main(String[] args) throws SQLException {
		//1.创建数据库连接的驱动(干活儿的对象)
		DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		//2.创建连接
		//三个参数的
		/*
		 * 第一个参数:数据库的访问路径
		 * 第二个参数:用户名
		 * 第三个参数:密码
		 */
		Connection connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "123456");
		//3.创建小车
		Statement statement = connection.createStatement();
		//绑定sql语句
		String sql = "select empno,ename,job from emp";
		ResultSet set =  statement.executeQuery(sql);
		//4.卸货
		while (set.next()) {//开始指针指向表头,调用next方法会使指针向下移动一行,判断当前行是否有数据,如果有,返回true,没有返回false
			//根据sql语句中字段的下标取值,默认从1开始
//			Object object = set.getObject(1);
//			//根据字段的名字取值
//			Object object2 = set.getObject("empno");
//			System.out.println(object+"    object2:"+object2);
			int empno  = set.getInt("empno");
			String ename = set.getString("ename");
			String job = set.getString("job");
			
			System.out.println(empno+"   ename:"+ename+"    job:"+job);
			
		}
		//5.关闭资源
		connection.close();
		statement.close();
		set.close();
	}
}

优化一

Class.forName("com.mysql.jdbc.Driver");
//Connection connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "123456");
		//两个参数:
//		Properties properties = new Properties();
//		//key是固定的
//		properties.setProperty("user", "root");
//		properties.setProperty("password", "123456");
//		Connection connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", properties);
		//一个参数
		Connection connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/db2?user=root&password=123456");
//增删改
		//增加
		//增删改共用executeUpdate方法
		String sql = "insert into emp(empno,ename,job) values(100,'bing','前台')";
		//通过返回值判断操作是否成功:0失败    >=1成功
		int value = statement.executeUpdate(sql);

优化二

	//4.卸货
		List<Emp> list = new ArrayList<>();
		while (set.next()) {//开始指针指向表头,调用next方法会使指针向下移动一行,判断当前行是否有数据,如果有,返回true,没有返回false			
			Emp emp = new Emp();

			emp.setEmpno(set.getInt("empno"));
			emp.setEname(set.getString("ename"));
			emp.setJob(set.getString("job"));
			
			list.add(emp);
		}

优化三 异常处理

//5.关闭资源
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if (statement != null) {
				try {
					statement.close();
				} catch (SQLException e2) {
					// TODO: handle exception
				}
			}
			
			if (set != null) {
				try {
					set.close();
				} catch (SQLException e2) {
					// TODO: handle exception
				}
			}
			

最终

public class DBUtil {
	static String mydriver;
	static String myurl;
	static String myuser;
	static String mypwd;
	static {
		//读取DBConfig文件的内容
		//默认识别的路径是当前的工程
		ResourceBundle resourceBundle = ResourceBundle.getBundle("DBConfig");
		mydriver = resourceBundle.getString("driver");
		myurl = resourceBundle.getString("url");
		myuser = resourceBundle.getString("user");
		mypwd = resourceBundle.getString("pwd");
		//1.创建数据库连接的驱动(干活儿的对象)
		//反射   :可以节省空间,方便开发
		try {
			Class.forName(mydriver);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	//2.创建连接
	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(myurl, myuser, mypwd);
	}
	
	public static void closeAll(Connection connection,Statement statement,ResultSet set) {
		//5.关闭资源
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		if (statement != null) {
			try {
				statement.close();
			} catch (SQLException e2) {
				// TODO: handle exception
			}
		}
		
		if (set != null) {
			try {
				set.close();
			} catch (SQLException e2) {
				// TODO: handle exception
			}
		}
	}
}

JDBC

​ 以下是优化后的代码,使用DBUtil将反射驱动,创建连接(通过读取ResourceBundle resourceBundle(""))的.properties文件 关闭连接方法封装在一个类中.


//反射   :可以节省空间,方便开发

Class.forName("com.mysql.jdbc.Driver");
//2.创建连接	
Connection connection =  DriverManager.getConnection(myUrl, myUser, myPwd);	
    //读取properties文件的内容
    ResourceBundle resourceBundle =ResourceBundle.getBundle("DBConfig");
//1.这里不需要写路径(默认路径是当前的工程),//2.不需要写后缀名
//3.创建小车
statement = connection.createStatement();

		mydriver = resourceBundle.getString("driver");
		myurl = resourceBundle.getString("url");
		myuser = resourceBundle.getString("user");
		mypwd = resourceBundle.getString("pwd");
ResultSet set =  statement.executeQuery(sql);
ResultSet的性质
set.next()//开始指针指向表头,调用next方法会使指针向下移动一行,判断当前行是否有数据,如果有,返回true,没有返回false	
    //set.next() 相当于原来的hasnext()
    //原来的 nextint()等方法在set.getint()等方法中实现

jdbc sql注入:

String sql = “select * from user where name=’”+username+"’ and password=’"+pwd+"’";

statement = connection.createStatement();
验证客户端的账号,密码
错误名称:sql注入
账号:chenchen
密码:’ or 1=‘1
sql语句:select * from user where name=‘chenchen’ and password=’’ or 1=‘1’
这是,where后面变成了衡真,所以跟没有写where没有区别,会将所有的结果查出来
为了避免这一情况,防止密码填写,sql注入问题

String sql = "select * from user where name=? and password=?";//?是占位符
			//3.创建小车绑定sql语句
			PreparedStatement statement = connection.prepareStatement(sql);
			//第一个参数是select语句中?的位置,默认从1开始
			statement.setString(1, username);
			statement.setString(2, pwd);
			//前面已经绑过小车了,这里不需要再次绑定
		    set = statement.executeQuery();

PreparedStatement (创建是就加入sql参数,通过setString()等方法传入sql中的?(占位符))(在结果集时executeQuery不需要传入sql)和 statement(创建空参,在结果集时executeQuery传入sql,)

事务:

事务:

事务:是逻辑上的一组操作,默认一个sql语句对应一个事务
一个事务中操作的特点:要么全成功,要么全不成功–保持一致.组成这组操作的各个单元,要么全成功,要么全不成功。

Mysql中的事务

时间线程1线程2说明
t1begin;
t2select * from account where name=‘zs’; 结果1000块
t3begin;
t4update account set money=money+100 where name=‘zs’;
t5select * from account where name=‘zs’; 结果1100块读到了另一个线程未提交事务的数据。赃读发生了
t6commit;
t7select * from account where name=‘zs’; 结果1100块读到了另一个线程提交事务的update数据。不可重复读发生了
t8insert into account values(4,‘zl’,1000); 执行insert语句插入数据,事务自动提交了
t9select * from account; 查到4条数据读到了另一个线程自动提交事务的insert语句数据。虚读发生了
t10commit;

a、mysql引擎是支持事务的

b、mysql默认自动提交事务。每条语句都处在单独的事务中。(即在jdbc中写开启事务的情况,如果发生异常,使得(后续的正常执行,只是正常的事务的结果没执行.)后续的所有sql语句的都在一个事务中,没有事务结束语句,那么后续的所有sql都不执行,(此时需要回滚(在异常处理机制中))

c、手动控制事务

开启事务:start transaction | begin

提交事务:commit

回滚事务:rollback

事务的相关方法都在connection中,

//在开启事务之前可以手动设置隔离级别--最高级别
			connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
			
			//手动开启事务--将默认的自动提交关闭,相当于开启了手动提交
			connection.setAutoCommit(false);
			//张三借钱
			String sql = "update acount set money=money-1000  where name=?";
			pStatement  = connection.prepareStatement(sql);
		    pStatement.setString(1, "zhangsan");
		    pStatement.executeUpdate();
		    
		  
		    
		    //李四收钱
		    sql = "update acount set money=money+1000  where name=?";
			pStatement  = connection.prepareStatement(sql);
		    pStatement.setString(1, "lisi");
		    pStatement.executeUpdate();
		    
		    //提交事务-将借钱与收钱手动放在一个事务中
		    connection.commit();
catch (ArithmeticException e) {
			//当发生这个异常的时候,让事务回滚:让当前事务退回到开启事务之前,当前对事务的所有操作失效
			try {
				connection.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

数据库通过设置事务的隔离级别防止以上情况的发生

事务的特性:ACID

A原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

C一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。转账前和转账后的总金额不变。

I隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

D持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事务的隔离级别

赃读:指一个事务读取了另一个事务未提交的数据。

不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。一个事务读取到了另一个事务提交后的数据。(update)

虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。 (insert)

数据库通过设置事务的隔离级别防止以上情况的发生:

1、READ UNCOMMITTED: 赃读、不可重复读、虚读都有可能发生。

2、READ COMMITTED: 避免赃读。不可重复读、虚读都有可能发生。(oracle默认的)

4、REPEATABLE READ:避免赃读、不可重复读。虚读有可能发生。(mysql默认)

8、SERIALIZABLE: 避免赃读、不可重复读、虚读。

级别越高,性能越低,数据越安全

注意:mysql的事务隔离等级默认是4,oracle的事务隔离等级默认是2

mysql中:

查看当前的事务隔离级别:SELECT @@TX_ISOLATION;

更改当前的事务隔离级别:SET TRANSACTION ISOLATION LEVEL 四个级别之一。

设置隔离级别必须在事务之前

连接池:

可以控制连接数据库和服务器访问的个数和次数.

解决建立数据库连接耗费资源和时间很多的问题,提高性能。

最大等待时间: (请求超时,服务器忙)

Dao层:

datasource:接口 (连接池规范接口)一般都叫数据源。

对于连接池,在多线程的环境下,和线程一样数据库的创建和关闭也需要的大量的资源和时间,所以连接池中存在大量的线程可以重复利用.

编写数据源时遇到的问题及解决办法:

即close()方法,如果正常调用close方法,会直接释放连接.但是我们使用的连接池,需要将连接重复的利用,所以不可能直接释放.

解决办法:

装饰设计模式:使用的频率很高

目的:改写已存在的类的某个方法或某些方法,装饰设计模式(包装模式)

口诀:

1、编写一个类,实现与被包装类相同的接口。(具备相同的行为)

2、定义一个被包装类类型的变量。

3、定义构造方法,把被包装类的对象注入,给被包装类变量赋值。

4、对于不需要改写的方法,调用原有的方法。

5、对于需要改写的方法,写自己的代码。

默认适配器:装饰设计模式一个变体

ConnectionWarper 本身就是一个装饰类(适配器类),

常用的数据源配置(日后都使用数据源,一定要配置一下)

连接池:DBCP:APache推出的连接池 C3P0:

dbcp: properties.load(DBCPUtil.class.getClass

1.导入三方架包

2.编写配置文件:配置文件的类型必须是properties,名字随意,位置随意

3.创建DBCPUtil工具类

注意点:在释放连接的close()的方法中,close()方法是重写过的方法,即(close的意思是将连接放回连接池)

properties.load(DBCPUtil.class.getClassLoader().getResourceAsStream(“dbcpconfig.properties”));理解:字节码文件对象的类加载器加载流文件

private static DataSource dataSource;
	//2.连接配置文件
	static{
		Properties properties = new Properties();
		try {
			//获取配置信息,将内容保存到properties对象里面
			properties.load(DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//得到连接池对象,将配置信息指定给连接池对象
		try {
			dataSource = BasicDataSourceFactory.createDataSource(properties);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	//3.获取连接
	public static Connection getConnection() throws SQLException {
		return dataSource.getConnection();
	}
	//4.释放连接
	public static void release(Connection conn,PreparedStatement statement,ResultSet set) {
		if (conn != null) {
			
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
			
			if (statement != null) {
				
			try {
				statement.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
			
			if (set != null) {
				
			try {
				set.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
	}
}

c3p0-config.xml(必须在当前目录下,自动加载)

dbcp(名字随便写,位置随便写)

DBUtils 介绍 apache

DBUtils是java编程中的数据库操作实用工具,小巧简单实用

C3P0:

注意事项:

1.系统会自动读取c3p0-config.xml文件:a.文件的名字要固定-c3p0-config.xml b.文件的位置也是固定的–src下面

2.一般会自己创建xml配置文件,按照要求创建即可.

3.优势:又有不需要自己关联配置文件,所以使用更方便

//1.创建池子(数据源)---创建了数据源之后,系统会自动添加连接
	private static ComboPooledDataSource dataSource =  new ComboPooledDataSource();
	//向池子中添加连接---系统会自动读取c3p0-config.xml文件:a.文件的名字要固定-c3p0-config.xml    b.文件的位置也是固定的--src下面
	//2.获取连接
	public static Connection getConnection() throws SQLException {
		return dataSource.getConnection();
	}
	//3.释放连接
	public static void release(Connection conn,PreparedStatement statement,ResultSet set) {
		if (conn != null) {
			
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
			
			if (statement != null) {
				
			try {
				statement.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
			
			if (set != null) {
				
			try {
				set.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
	}
}

DButils

实现了对PreparedStatement类和resultset的封装(简化操作)

@Test:他下面的方法是测试的方法,这个方法可以直接被调用,但是必须是在测试的环境下

注意点:1.方法不能有参数 2.不能有返回值

DBUtils是java编程中的数据库操作实用工具,小巧简单实用。

DBUtils封装了对JDBC的操作,简化了JDBC操作。可以少写代码。

1.对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;

2.对于数据表的写操作,也变得很简单(只需写sql语句)

3.可以使用数据源,使用JNDI,数据库连接池等技术来优化性能–重用已经构建好的数据库连接对象

QueryRunner类

//写sql语句
		String sql = "insert into user(id,name,password) values(?,?,?)";
		
		//第一种:使用的是无参的QueryRunner()方法----为了更加方便的使用事务,因为可以直接获取到Connection对象
		//1.创建干活的的对象--QueryRunner
		QueryRunner qRunner = new QueryRunner();
		//2.获取连接对象
		Connection connection = C3P0Util.getConnection();
		//3.调用update()方法实现对数据库的访问
		int num = qRunner.update(connection, sql,6,"马六","345");
		if (num >0) {
			System.out.println("增加成功");
		}else {
			System.out.println("增加失败");
		}
		
		//第二种:使用的是有参的QueryRunner()方法--参数是数据源
		//1.创建干活儿的对象并绑定数据源
//		QueryRunner qRunner2 = new QueryRunner(C3P0Util.getDataSource());
//		//2.调用update()方法
//		int num2 = qRunner2.update(sql,7,"马六1","3451");
//		if (num2 >0) {
//			System.out.println("增加成功");
//		}else {
//			System.out.println("增加失败");
//		}
	}
	
	//批量增加--只能同时执行一种操作
	@Test
	public void test3() throws SQLException{
		QueryRunner qRunner2 = new QueryRunner(C3P0Util.getDataSource());
		
		//创建一个二维数组装 数据
		Object[][] params = new Object[3][];
		for (int i = 0; i < params.length; i++) {
			params[i] = new Object[]{i+8,"bingbing"+i,"333"+i};
		}
		
		//写sql语句
	    String sql = "insert into user(id,name,password) values(?,?,?)";
		int[] nums = qRunner2.batch(sql, params);
	}
}

ResultSetHandler接口

注意:在QueryRunner类 中实现增删改(update的方法(sql ,可变参数),)

qRunner2.query(sql, Object[] params, ResultSerHandler) ResultSerHandler子类接口类型决定了返回值得类型.

	QueryRunner qRunner2 = new QueryRunner(C3P0Util.getDataSource());
		ResultSetHandler handler = 
		new ResultSetHandler<List<User>>() {

			/*
			 * 这是真正处理数据的方法
			 * 参数就是得到的结果集
			 * 这个方法会被自动调用
			 */
			@Override
			public List<User> handle(ResultSet set) throws SQLException {
				List<User> list = new ArrayList<User>();
				
				while (set.next()) {
					User user = new User();
					user.setName(set.getString("name"));
					user.setPassword(set.getString("password"));
					
					list.add(user);
				}
				
				return list;
			}
		};
		
		List<User> list = qRunner2.query("select * from user", handler);
		
		System.out.println(list);
	}
	
	//@Test
	//返回所有的数据--将记录装入模型,将模型装入list
	
	public void test2() throws SQLException{
		QueryRunner qRunner2 = new QueryRunner(C3P0Util.getDataSource());
																				//<对应模型的类>(模型的字节码文件)
		List<User> list = qRunner2.query("select * from user", new BeanListHandler<User>(User.class));
		System.out.println(list);
	}
	
	@Test
	//返回一条记录-得到的是模型
	public void test3() throws SQLException{
		QueryRunner qRunner2 = new QueryRunner(C3P0Util.getDataSource());
		User user = qRunner2.query("select * from user where id=?", new BeanHandler<User>(User.class),1);
		System.out.println(user);
	}
	
	//取一条记录
	//ArrayHandler:只能返回一个记录,并且没有存储到javabean中,默认返回的是第一个记录
	//@Test
	public void test13() throws SQLException{
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		//
		Object[] objects = qRunner.query("select * from user", new ArrayHandler());
		
		for (Object object : objects) {
			System.out.println(object);
		}
	}
	
	
	//取多条记录
	//ArrayListHandler:可以返回查到的多行记录,将一条记录的每个字段存储到一个数组中,再将这些数组放入一个集合中,并返回
	//@Test
	public void test4() throws SQLException{
		
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		
		List<Object[]> list = qRunner.query("select * from user", new ArrayListHandler());
		
		for (Object[] objects : list) {
			for (Object object : objects) {
				System.out.print(object+"  ");
			}
			System.out.println();
		}
		
	}
	
	//ColumnListHandler:根据指定的列数,取某一列的数据,封装到list中返回
	//@Test
	public void test5() throws SQLException{
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		//参数的意思是:指定查询的列号---从1开始计数
		List<Object> list = qRunner.query("select * from user", new ColumnListHandler(2));
		
		for (Object object : list) {
			System.out.print(object+"  ");
		}
	}
	
	//KeyedHandler:可以取多条记录,每一条记录被封装在了map中,然后再将所有的map对象放在一个大的map中,让每条记录中的某个字段充当外层map的key
	//注意:如果充当key的字段有重复,和面的记录会将前面所有的记录覆盖
	//@Test
	public void test6() throws SQLException{
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		
		Map<Object, Map<String, Object>> map=qRunner.query("select * from user", new KeyedHandler(5));
		
		for(Map.Entry<Object, Map<String, Object>> en:map.entrySet()){
			Object key = en.getKey();
			Map<String, Object> subMap = en.getValue();
			
			for(Map.Entry<String, Object> suben:subMap.entrySet()){
				String subkey = suben.getKey();
				Object value = suben.getValue();
				
				System.out.print(subkey+"="+value+"   ");
			}
			System.out.println("  key="+key);
		}
	}
	
	//只能取一条记录
	//MapHandler:将一条记录的字段和对应的值封装到一个map中,返回,默认取的是第一行
	//@Test
	public void test7() throws SQLException{
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		
		Map<String, Object> map = qRunner.query("select * from user", new MapHandler());
		
		for(String key:map.keySet()){
			System.out.print(key+"="+map.get(key)+"   ");
		}
	}
	
	
	//MapHandler:将一条记录的字段和对应的值封装到一个map中,再将所有的map放到一个list中并返回
	//@Test
	public void test8() throws SQLException{
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		List<Map<String, Object>> list = qRunner.query("select * from user", new MapListHandler());
		
		for (Map<String, Object> map : list) {
			for(String key:map.keySet()){
				System.out.print(key+"="+map.get(key)+"   ");
			}
			
			System.out.println();
		}
	}
	
	//取第一行的某一列的值
	//ScalarHandler:适合取单行单列的数据
	@Test
	public void test9() throws SQLException{
		QueryRunner qRunner = new QueryRunner(C3P0Util.getDataSource());
		//参数:代表第一行对应的行号,取这个列下的数据,若果不写,默认取第一列
		//Object object = qRunner.query("select * from user", new ScalarHandler(3));
		//与聚合函数联合使用,参数可以取的列是select后面可以获取的列数
		Object object1 = qRunner.query("select *,count(*) from user", new ScalarHandler(6));
		System.out.println(object1);
	}
}

BeanListHandler(User.class)

new BeanHandler(User.class)

new ArrayHandler() 一行 一位数组

new ArrayListHandler() 多行 多维数组

new ColumnListHandler(2) 某一行的具体数组

new KeyedHandler(5) 双层MAP Map<Object, Map<String, Object>>

new MapHandler() 将一条记录的字段和对应的值封装到一个map中,返回,默认取的是第一行

new MapListHandler() map存一行,list存多行

new ScalarHandler(6) 取第一行的某一列的值
//ScalarHandler:适合取单行单列的数据

DBUtils类

QueryRunner

QueryRunner中提供对sql语句操作的API.

它主要有三个方法

query() 用于执行select

update() 用于执行insert update delete

batch() 批处理

new QueryRunner(); 它的事务可以手动控制。也就是说此对象调用的方法(如:query、update、batrch)参数中要有Connection对象。

new QueryRunner(DataSource ds); 它的事务是自动控制的。一个sql一个事务。此对象调用的方法(如:query、update、batrch)参数中无需Connection对象。

ResultSetHandler类

用于定义select操作后,怎样封装结果集.

DbUtils类

它就是一个工具类,定义了关闭资源与事务处理的方法

tomcat服务器

Tomcat:Servlet/Jsp容器,轻量级服务器.

直接解压到指定目录即可。(注:目录不要太深;目录不要有中文或空格)

如果tomcat端口被占用,解决办法

> 修改F:\apache-tomcat-7.0.52\conf\server.xml

第70行:

> 关闭端口号对应的进程:

cmd–>netstat -ano -->找到pid–>任务管理器–>显示pid–>关闭进程

应用:

MyApp

1.html

Css

myStyle.css

Js

My.js

WEB-INF : 注意:固定写法。此目录下的文件不能被外部直接访问。

classes: 我们编写的程序代码。.class文件

lib : 应用需要用的jar文件
web.xml : 应用的配置信息

手动部署

> 开放目录部署方式

首先在F:\apache-tomcat-7.0.52\webapps下自己创建一个文件夹

然后把应用下webroot里面的内容直接复制到这个文件夹里面。

> 把应用打成war包。

打war包命令:jar -cvf MyApp.war .

把war包直接复制到F:\apache-tomcat-7.0.52\webapps下,应用自动解压

注:webapps目录下有几个目录就代表有几个应用;webapps就默认代表服务器的根目录。

自动部署

Tomcat服务器与MyEclipse集成(一定要掌握)

1、Tomcat集成

2、创建web应用

3、部署

修改myeclipse自带tomcat端口号方法:在myEclipse界面的左边,Servers->MyEclipse Tomcat v7.0-config->server.xml 进到里面修改端口号(20,63,83行处)

http:

概述:超文本传输协议,默认是html,用于web浏览器和web服务器的通信

post:请求体

get:请求行

URI:统一资源定位符,去协议和Ip地址

从第2行到空行处,都叫消息头

Accept:浏览器可接受的MIME类型

告诉服务器客户端能接收什么样类型的文件。

Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集

Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip

Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。 可以在浏览器中进行设置。

Host:初始URL中的主机和端口

Referrer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面

Content-Type:内容类型

告诉服务器浏览器传输数据的MIME类型,文件传输的类型

application/x-www-form-urlencoded

If-Modified-Since: Wed, 02 Feb 2011 12:04:56 GMT利用这个头与服务器的文件进行比对,如果一致,则从缓存中直接读取文件。

User-Agent:浏览器类型.

Content-Length:表示请求消息正文的长度

Connection:表示是否需要持久连接。如果服务器看到这里的值为“Keep -Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接

Cookie:这是最重要的请求头信息之一 (在讲会话时解析)

Date:Date: Mon, 22 Aug 2011 01:55:39 GMT请求时间GMT

1、消息正文: 当请求方式是POST方式时,才能看见消息正文

uName=tom&pwd=123

httprequest:类

http

**1、**响应消息行

第一行:

HTTP/1.1 200 OK

协议/版本 响应状态码 对响应码的描述(一切正常)

响应状态码:

常用的就40多个。

200(正常) 一切正常

302/307(临时重定向)

304(未修改)

表示客户机缓存的版本是最新的,客户机可以继续使用它,无需到服务器请求。

404(找不到) 服务器上不存在客户机所请求的资源。

500(服务器内部错误)

2、响应消息头

Location: http://www.it315.org/index.jsp指示新的资源的位置

通常和302/307一起使用,完成请求重定向

Server:apache tomcat指示服务器的类型

Content-Encoding: gzip服务器发送的数据采用的编码类型

Content-Length: 80 告诉浏览器正文的长度

Content-Language: zh-cn服务发送的文本的语言

Content-Type: text/html; charset=GB2312服务器发送的内容的MIME类型

Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT文件的最后修改时间

Refresh: 1;url=http://www.it315.org指示客户端刷新频率。单位是秒

Content-Disposition: attachment; filename=aaa.zip指示客户端下载文件

Set-Cookie:SS=Q0=5Lb_nQ; path=/search服务器端发送的Cookie

Expires: -1

Cache-Control: no-cache (1.1)

Pragma: no-cache (1.0) 表示告诉客户端不要使用缓存

Connection: close/Keep-Alive

Date: Tue, 11 Jul 2000 18:23:51 GMT

3、响应正文

和网页右键“查看源码”看到的内容一样。

Get和Post的区别:

  1. 相同点

都是请求方式(客户端给服务器端发数据)

  1. 不同点:

​ get会将内容放在请求消息行,post放在请求消息体

get的速度比post快

使用get适合传输数据量小的数据,<=1kb.post适合传输数据量大的数据

使用get安全性比post低

默认是get方式

服务器:servlet

servlet就是运行在WEB服务器的小型java程序(即:服务器端的小应用程序),servlet通过HTTP(超文本传输协议)接受和响应来自web客户端的请求

用户什么时候提交功能,servlet(里面五个方法,一个生命周期)什么时候(服务器)创建对象.

第一种创建servlet的方法,实现servlet接口(重写五个方法)

第二种创建servlet的方法

这是ServletDemo2继承了GenericServlet ,实现了Servlet接口

在GenericServlet里面,已经将Servlet的所有方法都实现了,我们在实现功能的时候,只需要创建

GenericServlet的子类,在子类中写我们需要的方法即可.我们把这种模式成适配器模式

第三种创建servlet的方式

默认的请求方式是get

ServletDemo3与HttpServlet之间是模板模式

ServletDemo3继承自HttpServlet–继承自GenericServlet–实现了Servlet

ServletConfig:一个servlet对象对应一个config,通过config只能访问当前servlet内部的数据(局部的数据)

ServletContext:一个应用对应一个context对象,通过他可以获取全局的数据

ServletConfig的使用

1.在init方法中获取对象

2.在doget中使用

web.xml

名称绑定servlet

filter4 com.qianfeng.filter.Filter4 filter4 /* ServletDemo1 com.qianfeng.servlet.ServletDemo1 ServletDemo2 com.qianfeng.servlet.ServletDemo2 ServletDemo3 com.qianfeng.servlet.ServletDemo3 ServletDemo1 /ServletDemo1 ServletDemo2 /ServletDemo2 ServletDemo3 /ServletDemo3 index.html index.htm index.jsp default.html default.htm default.jsp ```

MVC:

control.service层逻辑层判断,

control.servlet 层(javaweb为页面处理和参数处理)

dao sql语句 domian表页模型 (sql语句提交)

util:底层(数据库连接,)

自定义异常类

request

注解:

Java5开始 Java支持对元数据的支持, 就是是Annotation(注解)

元数据: metadata描述数据的数据
注解:标签 Annotation
所有的Annotation都是java.Lang.annotation.Annotation接口的子接口
所有Annotation是一种特殊的接口

回顾注解:
@Override -->重写
@Deprecated --> 过时的
@SuppressWarings --> 压制警告
既可以放在方法上,放在变量上,放在类或接口上
使用注解需要注意:必须有三个参与才有意义
1.有注解的本身
2.被放置的位置(类,属性,方法,接口,参数上,包等等)
3.由第三方程序使用反射的手段赋予注解特殊的功能

Java5之前自带注解

Java5中提供的

@Override --> 限定子类重写父类的方法,只能在方法上
@Deprecated --> 标记当前方法已经过时,不推荐使用 只能用在方法上
@SuppressWarings --> 抑制编译器发出多种警告
@SuppressWarings(value=“all”) 可以应用在方法,类,属性上
Java7中提供了一个
@SafeVarargs 抑制堆污染
​ 当方法使用可变参数和泛型参数,此时就会造成堆污染 Arrays.asList
Java8中提供一个
@FunctionalInteFace 函数式编程 -->所用在接口上

元注解:

描述注解的注解,用来限定注解可以贴在哪些程序元素上,还可以指定注解存在的周期
@Retention: 表示注解可以保存在哪一个代码时期. 保存的时期的值,封装在RetentionPolicy枚举类中:
注意:一般的,自定义的注解,使用RUNTIME.(使用反射赋予注解功能)
SOUECE:注解只能存在于源文件中,一旦编译,在字节码中就消失了.
CLASS:(缺省),注解可以存在于源文件和字节码文件中,一旦加载进JVM,在JVM中就消失了.
RUNTIME:注解可以存在于源文件,字节码文件,JVM中.

@Target:表示注解可以贴在哪些位置(类,方法上,构造器上等等).
位置的常量封装在ElementType枚举类中:
ElementType.ANNOTATION_TYPE只能修饰Annotation
ElementType.CONSTRUCTOR只能修饰构造方法
ElementType.FIELD只能修饰字段(属性),包括枚举常量
ElementType.LOCAL_VARIABLE只能修饰局部变量
ElementType.METHOD只能修饰方法
ElementType.PACKAGE只能修饰包(极少使用)
ElementType.PARAMETER只能修饰参数
ElementType.TYPE只能修饰类,接口,枚举

实现自己的注解:

语法格式:
@interface

@Retention(设置注解存储在的时间)
@Target(设置可以书写注解的位置)
public @interface 注解名{
​ 注解中的成员:
​ 抽象方法 --> (属性)
​ 数据类型 方法名() default 默认值;

}

使用@interface自定义注解,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

**定义注解格式:**public @interface 注解名 {定义体}

注解参数的可支持数据类型:

所有基本数据类型(int,float,boolean,byte,double,char,long,short)

String类型

Class类型

enum类型

Annotation类型

以上所有类型的数组

参数定义要点

只能用public或默认(default)这两个访问权修饰;

参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组;

如果只有一个参数成员,建议参数名称设为value();

注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或负数作为默认值是一种常用的做法。

ps:Spring框架中–> 注解最多 --> 动态代理模式 --> 内省模式JavaBean和Map互转

注解的作用:

java 注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解析注解 来使用这些数据),常见的作用有以下几种:

1.生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等;

2.跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;

3.在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;

4.自定义注解:可以应用到反射中,如实现实体类某些属性不自动赋值,或者验证某个对象属性完整性等所有的Annotation都是java.lang.annotation.Annotation的子接口

所有的直接都是特殊的接口.

import java.lang.annotation.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface testInter11 {
	String value () default "abc";
}
自定义用于方法上的注解
package com.qf.test;
import java.lang.annotation.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface testInter12 {
	String value1 () default "abc";
}
自定义注解的使用测试
package com.qf.test;

import java.lang.reflect.Method;

@testInter12(value1="ok")
public class Annotation1 {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
		//获取类的字节码文件对象
		Class<?> class1 = Class.forName("com.qf.test.Annotation1");
		//获取方法的字节码文件对象
		Method method = class1.getMethod("excute");
		//判断后面的注解是否在当前的类上
		if (class1.isAnnotationPresent(testInter12.class)) {
		//获取注解
			testInter12 a1 = (testInter12) class1.getAnnotation(testInter12.class);
			System.out.println("类上的value:"+a1.value1());
		}
		//判断后面的注解是否在当前的方法上
		if (method.isAnnotationPresent(testInter11.class)) {
			testInter11 t1 = method.getAnnotation(testInter11.class);
			System.out.println("方法上的注解:"+t1.value());
		}
	}
	//这里可以加(),也可以不加
	@testInter11()
	public   void excute() {
		System.out.println("haha");
	}
}

测试

软件测试: 这是软件生命周期中的一部分.—>好的软件都不是开发出来的,都是测试出来的.

黑盒测试: 软件测试工程师.
黑盒测试也称功能测试,它是通过测试来检测每个功能是否都能正常使用。
在测试中,把程序看作一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输入数据而产生正确的输出信息。
黑盒测试着眼于程序外部结构,不考虑内部逻辑结构,主要针对软件界面和软件功能进行测试。
黑盒测试是以用户的角度,从输入数据与输出数据的对应关系出发进行测试的。
很明显,如果外部特性本身设计有问题或规格说明的规定有误,用黑盒测试方法是发现不了的。

作用:
黑盒测试法注重于测试软件的功能需求,主要试图发现下列几类错误。
功能不正确或遗漏;
界面错误;
输入和输出错误;
数据库访问错误;
性能错误;
初始化和终止错误等。

白盒测试:由软件开放工程师来测试,只有自己开发的东西自己才知道是怎么运作的…
又称结构测试、透明盒测试、逻辑驱动测试或基于代码的测试。
它是按照程序内部的结构测试程序,通过测试来检测产品内部动作是否按照设计规格说明书的规定正常进行,
检验程序中的每条通路是否都能按预定要求正确工作。 这一方法是把测试对象看作一个打开的盒子,测试人员依据程序内部逻辑结构相关信息,设计或选择测试用例,对程序所有逻辑路径进行测试,通过在不同点检查程序的状态,确定实际的状态是否与预期的状态一致。

白盒测试是一种测试用设计方法,盒子指的是被测试的软件,白盒指的是盒子是可视的,你清楚盒子内部的东西以及里面是如何运作的。
"白盒"法全面了解程序内部逻辑结构、对所有逻辑路径进行测试。测试者必须检查程序的内部结构,从检查程序的逻辑着手,得出测试数据。

单元测试(junit)属于白盒测试.
ps:无论哪种测试,都需要给当前工程添加Junit–>jar

Junit3

Junit3是针对Java5之前的版本,没有注解,需要按照规范的形式来书写测试
遵守如下规则:
1.需要先将JUnit3添加到当前项目中
2.定义一个测试类,并让该测试类继承于TestCase类,测试的类型XXXTest
看Junit包Junit3Test

Junit4

Junit4是针对Java5之后的版本,使用的是注解,推荐
步骤
1.将Junit4添加到工程中
2.定义一个测试类,不在继承TestCase类,类名必须是XXXTest
3.所有的测试方法使用的注解
看Junit包Junit4Test

使用断言:

一般而言,使用断言是用来做单元测试使用
什么是断言呢:期望的结果和真实结果是一样的
期望值: 执行某个方法可以得到什么效果
实际值: 执行某个方法的到的实际效果
断言成功: 实际值和期望值相同 ,显示绿色
断言失败: 实际值和期望值不同 ,显示红色

①:Assert.assertEquals(message, expected, actual):比较的值,比较两个对象值存储的数据.
   三个参数:
​     message:  断言失败的提示信息,断言成功不会显示.
​     expected: 期望值
​     actual:   真实值
   若真实值和期望值相等,则断言成功.--->绿条
②:Assert.assertSame(message, expected, actual):比较地址,是同一个对象
  Assert.assertNotSame(message, expected, actual):断言不是同一个对象
③:Assert.assertTrue(message, condition):断言condition应该为TRUE.
④:Assert.assertFalse(message, condition):断言condition应该为FALSE.
⑤:Assert.assertNull(message, object):断言对象object为null.
⑥:Assert.assertNotNull(message, object):断言对象object不为null.

⑦:@T
est(expected=ArithmeticException.class)
   期望该方法报错,报错ArithmeticException.
⑧:@Test(timeout=400)
   期望该方法在400毫秒之内执行完成.
 类似资料: