JDBC自定义异常

来源:互联网 发布:淘宝客服应聘个人简历 编辑:程序博客网 时间:2024/05/16 14:55

1. JDBC

1.1. 自定义异常

在包com.njwangbo中新建一个包exception

新建自定义异常类LoginException.java

package com.njwb.exception;

/**

 * 自定义异常类,登录异常

 * @author Administrator

 *

 */

public class LoginException extends Exception {

public LoginException(String msg) {

super(msg);

}

}

新建自定义异常类 注册异常

package com.njwb.exception;

/**

 * 注册异常

 * @author Administrator

 *

 */

public class BizException  extends Exception{

private String code; //出错的状态码

private String msg; //出错的提示字符串

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

public String getMsg() {

return msg;

}

 

public void setMsg(String msg) {

this.msg = msg;

}

/**

 * 无参构造

 */

public BizException() {

super();

}

/**

 * 带参构造

 * @param code

 * @param msg

 */

public BizException(String code,String msg) {

super(msg);

this.code = code;

this.msg = msg;

}

}

 

package com.njwb.exception;

/**

 * 出错的提示信息

 * @author Administrator

 *

 */

public interface ErrorCode {

public final StringUSER_NAME_IS_NULL_ERROR_CODE="100001";

public final StringUSER_NAME_IS_EXIST_ERROR_CODE="100002";

public final StringUSER_NAME_IS_EXIST_ERROR_MSG="用户名已被注册";

public final StringUSER_NAME_OR_PWD_NULL ="用户名,密码不能为空";

}

1.2. 编写业务层

昨天的dao包中的接口和类主要负责和数据操作相关的事情(单张表的增,删,改,查)

在包com.njwangbo中新建一个包service

service包中的接口和类对dao的方法进行封装和调用,主要负责和业务逻辑相关的操作。

新建接口类UserService.java

该接口提供登录抽象方法,该方法传入2个参数(用户名,密码)

/**

 * 根据给定的用户名,密码登陆

 * @param userName

 * @param pwd

 * @return

 */

public abstract User login(String userName,Stringpwd) throws LoginException;

新建实现UserServiceImpl.java

* 登陆的时候思路  业务层,和逻辑相关的

 * 1.先验证用户名,密码是否符合规则,是否为空,长度是否超过指定位数,是不是以大写字母开头....(正则)比方说用户名,必须是5-16位大写小写字母数字下划线组合

 * 2.先根据用户名,去查找,是否有该用户存在,如果找不到,提示该用户不存在,请先去注册

 * 如果能找到,再去比对密码,密码正确,将该用户对象返回,如果密码错误,提示 密码错误

@Override

public User login(String userName, String pwd)throws LoginException {

User u = null;

if(StringUtil.isEmpty(userName) ||StringUtil.isEmpty(pwd)){

throw new LoginException("用户名,密码不能为空");

}else{

//先根据用户名去查询

User ui = ud.queryByName(userName);

if(ui==null){

throw new LoginException("该用户不存在,请先去注册");

}else{

//比对密码

if(ui.getPwd().equals(pwd)){

u = ui;

}else{

throw new LoginException("密码错误");

}

}

}

return u;

}

1.3. 登录功能

在测试类里,测试UserService层中的登录方法能否正常登录。

public static void main(String[] args) {

UserService us = new UserServiceImpl();

User u=null;

try {

u = us.login("jack1", "111111");

System.out.println("登录成功:"+u.getUserName()+"欢迎你登录");

} catch (LoginException e) {

//e.printStackTrace();

System.err.println("登录失败:"+e.getMessage());

}

}

1.4. 连接池

1.4.1. 引入文件

项目中引入commons-dbcp-1.4.jar和commons-pool-1.5.5

1.4.2. 创建连接池JdbcUtil.java

util中新建JdbcUtil.java,JdbcUtil中包括的对象:静态数据源,静态线程池,静态加载dataSource.properties文件的时候获取数据源对象ds。

提供的静态方法有:获取连接,关闭连接,关闭资源

连接池:连接池由容器提供的,用来管理池中连接对象,连接池自动分配连接对象.一旦某一个连接被使用,状态为忙碌,用完以后需要将连接归还给连接池,同时Connection状态为空闲。

连接池中的连接对象是由谁创建的的呢?

数据源(dataSource) javax.sql.DataSource接口负责建立与数据库的连接。

注意: 1.数据源 创建连接(Connection)对象

2.连接池用来管理(Connection)对象

3.在程序中使用BasicDataSourceFactory.createDataSource()方法获取数据源

代码如下:

package com.njwb.util;

import java.io.IOException;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

/**

 * 导包:javax.sql.*包下的

 * 获取连接对象

 * 关闭连接--->针对事务

 * 关闭(释放)资源--->针对每一次的sql语句

 * 配置文件,只能叫datasource.properties,也只能在src

 *

 * threadLocal:  get(),set(),remove()

 * 目的是让不同的线程都拥有一个属于自己的Connection

 * @author Administrator

 *

 */

public class JdbcUtil {

//声明数据源对象,用于创建连接

private static DataSourceds = null;

//给每一个线程,创建1个独立的变量副本,让每一个线程都有1个和数据库的连接(Connection),当前的线程变量

private static ThreadLocal<Connection>threadLocal = new ThreadLocal<Connection>();

//加载配置文件,获取数据源对象

static{

Properties p = new Properties();

try {

//通过p.load()方法加载配置文件(配置文件中的信息保存到p变量中)

p.load(JdbcUtil.class.getClassLoader().getResourceAsStream("datasource.properties"));

} catch (IOException e) {

System.out.println("加载datasource配置文件出错");

e.printStackTrace();

}

try {

//p传递给BasicDataSourceFactory,用于生成数据源对象ds

ds = BasicDataSourceFactory.createDataSource(p);

} catch (Exception e) {

System.out.println("构建数据源对象ds出错");

e.printStackTrace();

}

}

/**

 * 获取连接对象

 * @return

 */

public static Connection getConnection(){

//如果之前已经连接过数据库,可以直接获取到

Connection conn = threadLocal.get();

//如果之前没有连接过,获取的是null,这个时候

if(null==conn){

try {

//数据源ds生成连接对象conn

conn = ds.getConnection();

//将连接对象加入到threadLocal中(绑定到线程中),方便以后可以直接获取,不用再次创建

threadLocal.set(conn);

} catch (SQLException e) {

e.printStackTrace();

}

}

//return conn; //效果一样

return threadLocal.get();

}

/**

 * 关闭连接 ,在哪里调用, 提交事务,回滚事务的时候 会关闭连接

 */

public static void close(){

Connection conn = threadLocal.get();

try {

conn.close(); //close()方法已经被重写,归还给连接池 ,而不是直接销毁

} catch (SQLException e) {

e.printStackTrace();

}

//清除threadLocal中已经失效的连接对象

threadLocal.remove();

}

/**

 * 关闭资源(除了Connection以外的)

 * @param rs

 * @param ps

 */

public static void close(ResultSet rs,PreparedStatement ps){

if(null!=rs){

try {

rs.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

if(null!=ps){

try {

ps.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

}

新建datasource.properties配置文件,内容如下:

url=jdbc:mysql://127.0.0.1:3306/njwangbo1?useUnicode=true&characterEncoding=utf8

username=root

password=123456

driverClassName=com.mysql.jdbc.Driver

注意:ThreadLocal 也是为了解决线程并发(线程不安全)问题,同步机制,以时间换空间,同一份资源,多个线程排队访问。ThreadLocal为每一个线程提供1个独立的变量副本(从而隔离了多个线程对数据的访问冲突),以空间换时间,多个线程可以同时访问,互不影响。jdk5.0之后 支持泛型,ThreadLocal<T> ,ThreadLocal提供了线程安全的共享对象,在编写多线程时,会将一些线程不安全的变量封装进去。

1.4.3. 结果集映射RowMapper.java

util中新建RowMapper.java,RowMapper接口提供抽象方法,将结果集中的行映射成java中的一个对象返回,传入参数ResultSet .

public interface RowMapper {

/**

 * 将结果集rs中一行数据,映射成java中一个对象,具体映射成什么类的对象,就看谁实现了RowMapper接口

 * @param rs

 * @return

 */

public abstract Object mapperObject(ResultSet rs)throws SQLException;

}

1.4.4. 使用连接池实现查询JdbcTemplate.java

封装模板JdbcTemplate,该模板提供3个静态方法:增,删,改 方法,返回受影响的行数,静态查询方法,返回list集合。(可将设置占位符单独提成一个静态方法),每个方法的最后别忘了关闭资源。

public class JdbcTemplate {

/**

 * 增,删,改

 * @param sql

 * @param params

 * @return

 */

public static int executeUpdate(String sql,Object...params){

int count = 0;

//2.获取连接对象

Connection conn = JdbcUtil.getConnection();

PreparedStatement ps = null;

try {

//3.获取sql执行器

ps = conn.prepareStatement(sql);

//4.设置占位符

setParams(ps, params);

//5.执行,并返回处理结果

count = ps.executeUpdate();

} catch (SQLException e) {

e.printStackTrace();

}finally{

//6.关闭资源(除了Connection之外的)

JdbcUtil.close(null, ps);

}

return count;

}

/**

 *

 * @param sql

 * @param mapper

 * @param params

 * @return

 */

@SuppressWarnings("unchecked")

public static List executeQuery(String sql,RowMapper mapper,Object... params){

List list = new ArrayList();

//2.获取连接对象

Connection conn = JdbcUtil.getConnection();

PreparedStatement ps = null;

ResultSet rs = null;

try {

//3.获取sql执行器对象

ps = conn.prepareStatement(sql);

//4.设置占位符

setParams(ps, params);

//5.执行,并返回处理结果

rs = ps.executeQuery();

//遍历结果集,将数据封装成对应的对象,将该对象添加到集合中

while(rs.next()){

//这边有可能是User对象,也可能是Book对象,Account对象....

Object obj = mapper.mapperObject(rs);

list.add(obj);

}

} catch (SQLException e) {

e.printStackTrace();

}finally{

//6.释放资源

JdbcUtil.close(rs, ps);

}

return list;

}

/**

 * 私有的,设置占位符

 * @param ps

 * @param params

 * @throws SQLException

 */

private static void setParams(PreparedStatement ps,Object...params)throws SQLException{

if(params!=null && params.length>0){

for(int i=0;i<params.length;i++){

ps.setObject((i+1), params[i]);

}

}

}

}

1.4.5. 编写表的对象转换器UserMapper.java

位置Dao->impl->UserMapper.javaUserMapper实现把结果集中的行映射成User对象。

public class UserMapperimplements RowMapper {

/**

 * 结果集中的行,映射成1java对象

 * @throws SQLException

 */

@Override

public Object mapperObject(ResultSet rs)throws SQLException{

User u = new User();

u.setId(rs.getInt("t_id"));

u.setUserName(rs.getString("t_userName"));

u.setPwd(rs.getString("t_pwd"));

u.setCreateTime(rs.getTimestamp("createTime"));

return u;

}

}

1.4.6. 修改DAO层

修改UserDaoImpl,把添加用户方法,按名字查询用户,返回用户对象方法,按名字,密码查询用户,返回用户对象方法(至少改2个方法,剩余的其他方法作为课后作业完成),调用模板JdbcTemplate里封装里的方法。

package com.njwb.dao.impl;

import java.util.List;

import com.njwb.dao.UserDao;

import com.njwb.entity.User;

import com.njwb.util.JdbcTemplate;

/**

 * UserDao接口的实现类

 * @author Administrator

 *

 */

@SuppressWarnings("unchecked")

public class UserDaoImplimplements UserDao {

@Override

public boolean addUser(User u) {

String sql = "insert into t_user3(t_userName,t_pwd,createTime) values(?,?,now())";

int count = JdbcTemplate.executeUpdate(sql, u.getUserName(),u.getPwd());

return count>0;

}

@Override

public User queryByCondition(String name, String pwd) {

String sql = "select * from t_user3 where t_userName=? and t_pwd=?";

List<User> list = JdbcTemplate.executeQuery(sql, new UserMapper(),name,pwd);

if(list.size()>0){

return list.get(0);

}else{

return null;

}

}

@Override

public User queryByName(String name) {

String sql = "select * from t_user3 where t_userName=?";

List<User> list = JdbcTemplate.executeQuery(sql, new UserMapper(),name);

if(list.size()>0){

return list.get(0);

}

return null;

}

@Override

public List<User> queryAll() {

String sql = "select * from t_user3";

List<User> list = JdbcTemplate.executeQuery(sql, new UserMapper());

//方式2

//List<User> list = JdbcTemplate.executeQuery(sql, new UserMapper(), new Object[]{});

return list;

}

/**

 * limit 起始位置(索引从0开始)  截取几条数据   0,2    2页  2,2  34,2

 */ 

@Override

public List<User> queryByPageIndex(int index,int pageSize) {

String sql = "select *  from t_user3 limit ?,? ";

List<User> list = JdbcTemplate.executeQuery(sql, new UserMapper(), (index-1)*pageSize,pageSize);

return list;

}

}

1.5. 对象工厂

新建包factory,在包中新建ObjectFactory.java。

ObjectFactory.java文件,只是为了加载配置文件Object,读取到相应实现类(UserDaoImpl,UserServieceImpl,TransactionImpl)的全路径,获取到类对象,通过newInstance()方法获取该实现类的实例对象。通过对象工厂方式获取的类的实例对象的方法,和直接new()实例对象方法,实现效果相同。

通过输入流读取配置文件信息。

package com.njwb.factory;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.util.HashMap;

import java.util.Map;

/**

 * BufferedReader(Reader in)

 * InputStreamReader(InputStream in)

 *

 * 如果arr[1] com.njwb.dao.impl.UserDaoImplnew Instance()生成就是UserDaoImpl的实例  作用和  new UserDaoImpl() 相同

 * @author Administrator

 *

 */

public class ObjectFactory {

private static Map<String,Object>objs = new HashMap<String,Object>();

static{

//建立输入流和源文件之间的联系

BufferedReader br = new BufferedReader(new InputStreamReader(ObjectFactory.class.getClassLoader().getResourceAsStream("Object.properties")));

//声明String变量,用于接收读取的字符串

String data = null;

try {

while((data=br.readLine())!=null){

String[] arr = data.split("=");

//获取键

String key = arr[0];

//获取值

/*//获取类Class对象

Class cla = Class.forName(arr[1]);

//利用反射生成该类对应的实例(利用反射调用该类的无参构造创建实例)

Object value = cla.newInstance();*/

Object value = Class.forName(arr[1]).newInstance();

//将键-值添加到map

objs.put(key, value);

}

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}finally{

if(null!=br){

try {

br.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

public static Object getBean(String key){

return objs.get(key);

}

}

1.5.1. src下新建Object文件

userDao=com.njwb.dao.impl.UserDaoImpl

tx=com.njwb.transaction.impl.TransactionImpl

userService=com.njwb.service.impl.UserServiceImpl

配置的顺序和调用的顺序一致,如果不清楚,从dao层写无参构造写到Service层,在控制台查看调用的顺序 ,先配置dao层,再配置Service

举例:如果业务复杂的时候,需要配置多个类的Object文件,规律相同

1.5.2. 修改UserServiceImpl.java

新的方式获取 UserDao的实现类对象UserDaoImpl,对象工厂的方式获取,不再通过new()关键字获取

//private UserDaoud = new UserDaoImpl();

//private Transactiontx = new TransactionImpl();

private UserDao ud = (UserDao)ObjectFactory.getBean("userDao");

private Transaction tx = (Transaction)ObjectFactory.getBean("tx");

修改测试类里代码

//UserService us = new UserServiceImpl();

UserService us = (UserService)ObjectFactory.getBean("userService");

1.6. 事务

什么是事务?

事务是一种机制,一个操作序列,它包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。因此事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时,事务是作为最小的控制单元来使用的,它特别适用于多用户同时操作的数据库系统。例如:航空公司的订票系统,银行,保险公司以及证券交易系统等。

事是作为单个逻辑工作单元执行的一系列操作。一个逻辑工作单元必须有4个属性(简称ACID):

原子性

事务是一个完整的操作。事务的各元素是不可分的(原子的)。事务中的所有元素必须作为一个整体提交或回滚。如果事务中的任何元素失败,则整个事务将失败。以银行转账事务为例。如果该事务提交了,则这2个账户的数据将会更新。如果由于某种原因,事务在成功更新这2个账户之前终止了,则不会更新这两个账户的余额,并且会撤销对任何账户余额的修改,事务不能部分提交。

一致性

当事务完成时,数据必须处于一致状态。也就是说,在事务开始之前,数据库中存储的数据处于一致状态。在正在进行的事务中,数据可能处于不一致的状态,如果数据可能部分被修改。然而,当事务成功完成时,数据必须再次回到已知的一致状态。通过事务对数据所做的修改不能破坏数据,或者说事务不能使数据存储处于不稳定的状态。

以银行转账事务为例,在事务开始之前,所有账户余额的总额处于一致状态。在事务进行的过程中,一个账户余额减少了,而另一个账户余额尚未修改。因此,所有账户余额的总额处于不一致状态。事务完成以后,账户余额的总额再次恢复到一致状态。

隔离性

对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。修改数据的事务可以再另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。另外,当事务修改数据时,如果任何其他进程正在同时使用相同的数据,则直到该事务成功提交之后,对数据的修改才能生效。张三和李四之间的转账与王五和赵二之间的转账,永远是相互独立的。

持久性

事务的持久性指不管系统是否发生了故障,事务处理的结果都是永久的。

一个事务成功完成后,它对于数据库的改变是永久性的,即使系统出现故障也是如此。就是说,一旦事务被提交,事务的效果会被永久地保存在数据库中。

1.6.1. 新建事务接口

新建transaction包,新建transaction->Transaction.java接口,该接口中提供3个抽象方法 开始事务,提交事务,回滚事务。

package com.njwb.transaction;

public interface Transaction {

/**

 * 开启事务

 */

public abstract void begin();

/**

 * 提交事务

 */

public abstract void commit();

/**

 * 回滚事务

 */

public abstract void rollBack();

}

1.6.2. 实现接口

建包 transaction—impl ,在下面新建 TransactionImpl.java ,在TransactionImpl里实现开始方法里,获取到连接对象,设置自动提交为手动提交,

在提交方法里,获取到连接对象,正常提交,出现异常,抛出异常,最后关闭连接。回滚方法和提交方法类似。(获取连接对象,正常回滚,抛出异常,最后关闭连接)

package com.njwb.transaction.impl;

import java.sql.Connection;

import java.sql.SQLException;

import com.njwb.transaction.Transaction;

import com.njwb.util.JdbcUtil;

public class TransactionImpl implements Transaction {

@Override

public void begin() {

//将自动提交,设置成手动提交 ,默认是自动提交

Connection conn =JdbcUtil.getConnection();

try {

conn.setAutoCommit(false);

} catch (SQLException e) {

e.printStackTrace();

}

}

@Override

public void commit() {

Connection conn = JdbcUtil.getConnection();

try {

conn.commit();

} catch (SQLException e) {

e.printStackTrace();

}finally{

JdbcUtil.close(); //释放连接

}

}

@Override

public void rollBack() {

Connection conn =JdbcUtil.getConnection();

try {

conn.rollback();

} catch (SQLException e) {

e.printStackTrace();

}finally{

JdbcUtil.close();//释放连接

}

}

}

1.6.3. 在service层中加事务

service层的实现类UserServiceImpl中加入事务按情况确定能否注册成功(如果按姓名查询,不存在重名的情况,就可以注册成功),userService中添加注册方法,调用UserDao中添加用户方法。 service层新增注册方法(根据指定的用户名,密码,创建时间,注册新的用户对象,保存到数据库中)

注册方法没有添加事务时:

@Override

public boolean regist(User u)throws BizException {

boolean isSuccess = false; //该标志位用于表示是否注册成功

//验证是否为空

if(StringUtil.isEmpty(u.getUserName()) || StringUtil.isEmpty(u.getPwd())){

//之前是抛出的错误的提示信息用户名,密码不能为空”,而现在将错误的提示信息放到1ErrorCode接口中,public修饰的常量字符串

throw new  BizException(ErrorCode.USER_NAME_IS_NULL_ERROR_CODE, ErrorCode.USER_NAME_OR_PWD_NULL);

}

//先根据名字查询

User ui = ud.queryByName(u.getUserName());

if(null!=ui){

throw new  BizException(ErrorCode.USER_NAME_IS_EXIST_ERROR_CODE, ErrorCode.USER_NAME_IS_EXIST_ERROR_MSG);

}else{

isSuccess = ud.addUser(u);

}

return isSuccess;

}

注册方法添加事务后:

@Override

public boolean regist(User u)throws BizException {

tx.begin();

boolean isSuccess = false; //该标志位用于表示是否注册成功

//验证是否为空

if(StringUtil.isEmpty(u.getUserName()) || StringUtil.isEmpty(u.getPwd())){

//之前是抛出的错误的提示信息用户名,密码不能为空”,而现在将错误的提示信息放到1ErrorCode接口中,public修饰的常量字符串

throw new  BizException(ErrorCode.USER_NAME_IS_NULL_ERROR_CODE, ErrorCode.USER_NAME_OR_PWD_NULL);

}

//先根据名字查询

User ui = ud.queryByName(u.getUserName());

if(null!=ui){

throw new  BizException(ErrorCode.USER_NAME_IS_EXIST_ERROR_CODE, ErrorCode.USER_NAME_IS_EXIST_ERROR_MSG);

}else{

isSuccess = ud.addUser(u);

}

//判断标志位的值,如果新增成功,返回true,提交事务,否则回滚

if(isSuccess){

tx.commit();//一旦开启事务(设置了手动提交),只有执行commit,才能将数据真正的插入数据库中

}else{

tx.rollBack();

}

return isSuccess;

}

 

 

1.7. 分页查询

UserDao中

/**

 * 分页查询

 * @param index页码

 * @param pageSize页面的容量

 * @return

 */

public List<User> queryByPageIndex(int index,int pageSize);

UserDaoImpl中

/**

 * limit起始位置(索引从0开始)  截取几条数据   0,2    2页  2,2  34,2

 */ 

@Override

public List<User> queryByPageIndex(int index,int pageSize) {

tx.begin();

String sql = "select *  from t_user3 limit ?,? ";

List<User> list = JdbcTemplate.executeQuery(sql,new UserMapper(), (index-1)*pageSize,pageSize);

tx.commit();

return list;

}

UserService中

/**

 * 分页查询

 * @param index

 * @param pageSize

 * @return

 */

public List<User> queryByPageIndex(int index,int pageSize);

UserServiceImpl中

@Override

public List<User> queryByPageIndex(int index,int pageSize) {

List<User> list = ud.queryByPageIndex(index, pageSize);

return list;

}

TestUserServiceImpl03

public class TestUserServiceImp03 {

public static void main(String[] args) {

queryByIndex(2, 4);

}

public static void queryByIndex(int index,int pageSize){

UserService us = new UserServiceImpl();

List<User> list = us.queryByPageIndex(index, pageSize);

System.out.println("****"+list.size());

//遍历集合

for(User u:list){

System.out.println(u.getId()+"\t"+u.getUserName()+"\t"+u.getPwd());

}

}

}

1.8. 总结

com.njwb.entity  实体包  User.java 和数据库表中的字段一一对应的。 Book.java …

com.njwb.dao  接口  UserDao.java, BookDao.java …

com.njwb.dao.impl 接口的实现类   UserDaoImpl.java  BookDaoImpl.java

dao层可以抛出系统常见的异常 SQLException ,IOException …….., dao层抛出的异常,在service层捕获。

com.njwb.service 接口  UserService.java  ,BookService.java

com.njwb.service.impl 接口的实现类  UserServiceImpl.java, BookServiceImpl.java….

service层一般抛出自定义异常 ,在view层捕获

com.njwb.util 工具包   JdbcUtil.java(Connection)  ,

DataSource ds  从哪里来?

 ,通过导入第三方工具包  2个  ,将数据库的一些信息配置src下datasource.properties文件中(url,password,username,driverClassName),创建了Properties 对象

Properties p = new Properties(); p.load(输入流JdbcUtil.class.getClassLoader.getResourceAsStream(“datasource.properties”)); 配置文件的信息保存到p变量中,将p变量传递给BasicDatasourceFactory ,用于生成数据源对象ds   ds = BasicDataSourceFactory.createDataSource(p); BasicDataSourceFactory 位于org.apache.commons.dbcp.BasicDataSourceFactory包,跟哪个工具类有关(commons-dbcp-1.4.jar).

Connection conn 从哪里来?

conn都是在连接池里, 连接池里面存储多个Connection对象,如果哪个Connection conn在使用,状态就是忙碌,当jdbc操作全部做完以后,将Connection conn对象归还给连接池,这个时候该连接对象又是空闲状态。数据源ds生成Connection Connection  conn = ds.getConnection(),放入连接池

为什么将Connection对象封装进threadLocal ?  1.为了解决多线程并发的问题(为了保证线程安全的),threadlocal 表示当前的线程变量,为每一个线程提供一个单独的变量副本,客户端就是每一个线程,可以包装每一个线程单独享有一个和数据库的连接。

JdbcTemplate.java(2个方法,增,删,改; 查询) ,  RowMapper.java(行映射器 接口)

RowMapper.java关于该接口的实现类 UserMapper.java, BookMapper.java. 可以单独存储在com.njwb.mapper包下,或者存储在com.njwb.dao.impl包下。

com.njwb.view 层 ,页面 ,在这里做了测试

com.njwb.exception 包 下,存放的都是自定义异常类  LoginException.java, RegistException.java ,BizException.java ….

 

com.njwb.transaction 包 接口   Transaction.java ,接口 ,里面只有3个抽象方法 begin() 开启事务  commit()提交事务  rollBack()回滚事务

com.njwb.transaction.impl 实现类 TransactionImpl.java. 实现了3个抽象方法   ,在哪一层开启事务,提交事务,回滚事务Service层

 

com.njwb.factory 包  下 ObjectFactory.java  加载配置文件Object.properties (该配置文件名字随意取),读取配置文件的信息,将键-值保存到map中, 提供1个公有的静态方法,可以根据键获取值

userDao = com…….UserDaoImpl

tx=com…..Transaction.impl

userService=com……..UserServiceImpl

bookDao=com…..BookDaoIml

bookService=com……BookServiceImpl

categoryDao=com…..CategoryDaoImpl

categroyServcie=com….CategoryServiceImpl

…….

 

1.9. 多表联查,如何处理 ,级联删除

create table t_student06(

    t_stuNo  int  auto_increment primary key,

   t_name  varchar(20) ,

   t_phone  char(11),

   t_sex  char(2),

  t_gradeId int

)engine=InnoDB;

 

create table t_grade(

  t_id int auto_increment primary key,

  t_gradeName varchar(30),

createTime dateTime

)engine=InnoDB;

 

alter table t_student06 add foreign key(t_gradeId) references  t_grade(t_id);

 

插入数据 

insert into t_grade(t_gradeName,createTime)

values('二年级',now()),

('三年级',now()),

('四年级',now()),

('五年级',now());

 

insert into t_student06( t_name,t_phone,t_sex,t_gradeId)

values('bob','12121212','女',2),

('tom','12121212','女',3),

('helen','12121212','男',4),

('kate','12121212','女',5),

('jet','12121212','男',1);

两种思路

方式1:两表联查(查一次) 效率高

想显示的是 学生学号,姓名,电话,性别,年级名称  

//查询的是指定的列

select s.t_stuNo,s.t_name,s.t_phone,s.t_sex,g.t_gradeName ,g.t_id  

 from t_student06 s  inner join t_grade g on s.t_gradeId = g.t_id;

select s.*,g.*   from t_student06 s  inner join t_grade g on s.t_gradeId = g.t_id;

方式2:(2次)

select s.t_stuNo,s.t_name,s.t_phone,s.t_sex,s.t_gradeId

 from t_student06

select g.t_gradeName  from t_grade g where t_id=? ;

package com.njwb.entity;

public class Student {

private int stuNo;//学号

private String stuName;//学生姓名

private String stuPhone;//电话

private String sex;//性别

private Grade grade; //不要直接放gradeId

public int getStuNo() {

return stuNo;

}

public void setStuNo(int stuNo) {

this.stuNo = stuNo;

}

public String getStuName() {

return stuName;

}

public void setStuName(String stuName) {

this.stuName = stuName;

}

public String getStuPhone() {

return stuPhone;

}

public void setStuPhone(String stuPhone) {

this.stuPhone = stuPhone;

}

public String getSex() {

return sex;

}

public void setSex(String sex) {

this.sex = sex;

}

public Grade getGrade() {

return grade;

}

public void setGrade(Grade grade) {

this.grade = grade;

}

}

 

package com.njwb.dao.impl;

import java.sql.ResultSet;

import java.sql.SQLException;

import com.njwb.entity.Grade;

import com.njwb.entity.Student;

import com.njwb.util.RowMapper;

public class StudentMapper implements RowMapper {

/**

 * select s.t_stuNo,s.t_name,s.t_phone,s.t_sex,g.t_gradeName ,g.t_id  

 from t_student06 s  inner join t_grade g on s.t_gradeId = g.t_id;

 */

@Override

public Object mapperObject(ResultSet rs) throws SQLException {

Student stu = new Student();

stu.setStuNo(rs.getInt("t_stuNo"));

stu.setStuName(rs.getString("t_name"));

stu.setStuPhone(rs.getString("t_phone"));

stu.setSex(rs.getString("t_sex"));

//下面查询的年级编号,年级名称封装在Grade对象中

Grade g = new Grade();

g.setGradeName(rs.getString("t_gradeName"));

g.setId(rs.getInt("t_id"));

//grade数据要设置到stu对象上

stu.setGrade(g);

return stu;

}

}

//方式1

@Override

public List<Student> queryAllStudent() {

String sql = "select s.t_stuNo,s.t_name,s.t_phone,s.t_sex,g.t_gradeName ,g.t_id "

+" from t_student06 s  inner join t_grade g on s.t_gradeId = g.t_id";

List<Student> list = JdbcTemplate.executeQuery(sql, new StudentMapper(),new Object[]{});

return list;

}

 

//方式2

@Override

public List<Student2> queryStudent2() {

String sql = "select *  from t_student06";

List<Student2>  list = JdbcTemplate.executeQuery(sql, new Student2Mapper());

return list;

}

/**

 * 根据年级编号获取的年级名称

 */

@Override

public String getGradeNameById(int gradeId) {

String sql = "select g.t_gradeName,g.t_id,g.createTime  from t_grade g where t_id=?";

List<Grade> list = JdbcTemplate.executeQuery(sql, new GradeMapper(), gradeId);

if(!list.isEmpty()){

return list.get(0).getGradeName();

}

return null;

}

 

级联删除:

删除 年级表的一条记录

情况1:这条记录没有被外键表引用(t_student06)中

直接删除

情况2:这条记录被外键表引用(t_student06)中

先根据年级编号,在外键表中查询,是否有数据,如果有

这个时候提示用户,(该年级信息在外键表中有引用,是否确定删除?)询问是否继续删除?

是, 先删除外建表 t_student06中该年级编号下的信息,再删除主键表中的这条记录

 

select s.t_stuNo,s.t_name,s.t_phone,s.t_sex,g.t_gradeName ,g.t_id  

 from t_student06 s  inner join t_grade g on s.t_gradeId = g.t_id where t_gradeId=?;

 

delete from t_student06 where t_gradeId=?;

 

delete from t_grade where t_id=?;

 

设计方法 public List<Student> queryByGradeId(int gradeId);  如果list.size()>0,说明有引用

public boolean delStudentByGradeId(int gradeId); // 删除该年级编号下的学生信息

public boolean deGradeByGradeId(int gradeId) ;//删除主表

@Override

public boolean deGradeByGradeId(int gradeId) {

String sql = "delete from t_grade where t_id=?";

int count =JdbcTemplate.executeUpdate(sql, gradeId);

return count>0;

}

@Override

public boolean delStudentByGradeId(int gradeId) {

String sql = "delete from t_student06 where t_gradeId=?";

int count = JdbcTemplate.executeUpdate(sql, gradeId);

return count>0;

}

@Override

public List<Student> queryByGradeId(int gradeId) {

String sql = "select s.t_stuNo,s.t_name,s.t_phone,s.t_sex,g.t_gradeName ,g.t_id"+   

" from t_student06 s  inner join t_grade g " +

"on s.t_gradeId = g.t_id where t_gradeId=?";

List<Student> list = JdbcTemplate.executeQuery(sql,new StudentMapper() , gradeId);

return list;

}

public static void main(String[] args) {

UserService us = (UserService)ObjectFactory.getBean("userService");

Scanner input = new Scanner(System.in);

//查询所有的年级信息

List<Grade> gradeList = us.queryAllGrades();

System.out.println("年级编号\t年级名称\t");

for(int i=0;i<gradeList.size();i++){

System.out.println(gradeList.get(i).getId()+"\t"+gradeList.get(i).getGradeName());

}

System.out.print("请选择要删除的年级编号:");

int bianhao = input.nextInt();

//这个时候,根据年级编号取查询,是否被外建表引用

List<Student> stuList = us.queryByGradeId(bianhao);

if(stuList.size()>0){

System.out.println("该年级信息在外键表t_student06中有引用,是否确认删除(y:确认n:取消)?");

String isConfirm = input.next();

if(isConfirm.equalsIgnoreCase("y")){

//先删除外键表,再删除主键表

boolean flag1 = us.delStudentByGradeId(bianhao);

if(flag1){

boolean flag2 = us.deGradeByGradeId(bianhao);

System.out.println(flag2?"删除年级信息成功":"删除年级信息失败");

}else{

System.out.println("外建表中该"+bianhao+"下的学生信息删除失败");

}

}else{

System.out.println("你已取消删除");

}

}else{

//在外键表没有引用,直接删除

boolean flag2 = us.deGradeByGradeId(bianhao);

System.out.println(flag2?"删除年级信息成功":"删除年级信息失败");

}

}

注意:特殊情况,如果学生表t_student06t_result(成绩表)的外键表,这个时候在删除学生表信息时,肯定会出现主外键约束问题,删除顺序:先删除t_result表中的信息---再删除t_student06表中的---最后删除t_grade表中的信息(主外键引用的情况删除原则:先删除外建表,再删除主键表)

1.10. 作业

银行账户管理系统  ,分层设计,dao,service,view,事务加上,对象工厂获取对象

页面包含3个菜单 1.开户  2.转账  3.查询所有账户

设计表  用户编号,  账号   账户余额  该条记录创建时间

create table t_account(

t_id int  auto_increment primary key,

t_accNum varchar(30),

t_money  double,

t_createTime dateTime

)engine=InnoDB;

原创粉丝点击