JDBC(2)JAVA连接数据库之 分页操作、事务操作、代理模式


分页操作(排序)

与表相对应的用户类

package lesson2;

public class User {
private int id;
private String username;
private String userpass;
private String email;
public User (){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpass() {
return userpass;
}
public void setUserpass(String userpass) {
this.userpass = userpass;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}

public User(int id, String username, String userpass, String email) {
super();
this.id = id;
this.username = username;
this.userpass = userpass;
this.email = email;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", userpass="
+ userpass + ", email=" + email + "]";
}
}

与表相关操作的包装类(UserDao)

package lesson2;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
* 分页操作
* @author hh
*
*/

public class UserDao {
public List<User> findByPage(int page,int pageSize){
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<User> users = new ArrayList<User>();
try {
con = JdbcUitl.getConn();
int begin=(page-1)*pageSize+1;
int end = page*pageSize;
String sql="select id,username,userpass,email from" +
"(select id,username,userpass,email,rownum rn from bb_user order by id)" +
"where rn between ? and ?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, begin);
pstmt.setInt(2, end);
rs=pstmt.executeQuery();
while(rs.next()){
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setUserpass(rs.getString("userpass"));
user.setEmail(rs.getString("email"));
users.add(user);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUitl.close(pstmt, rs);
}
return users;
}
}

测试类

package lesson2;
import java.util.ArrayList;
import java.util.List;

public class JdbcDemo1 {
public static void main(String[] args) {
UserDao ud = new UserDao();
List<User> users =ud.findByPage(1,5);
for (User user : users) {
System.out.println(user);
}
}
}

事务

  • 创建一张表
  • 把表相关的操作封装
  • 只有提交事务之后,jabc才能操作数据
    • commit 提交 rollback 回滚
  • Connection 对象默认操作完后提交事务。
  • 如果不要自动提交,需要操作:con.setAutoCommit(false);

创建新的测试表格account 创建与之对应的数据类

package lesson2;

public class Account {
private int id;
private int money;
private String name;
public Account(){}

public Account(int id, int money, String name) {
super();
this.id = id;
this.money = money;
this.name = name;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Account [id=" + id + ", money=" + money + ", name=" + name
+ "]";
}

}

与表相关操作的包装类(AccountDao)

这可以看到没有关闭Connection对象,是为了后面的事务操作

package lesson2;

import java.nio.Buffer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.PseudoColumnUsage;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import oracle.net.aso.p;

public class AccountDao {
public int addAccount(Account account){
Connection con = null;
PreparedStatement pstmt = null;
int n = 0;
try {
con = JdbcUitl.getConn();
pstmt = con.prepareStatement("insert into account(id,money,name)values(?,?,?)");
pstmt.setInt(1, account.getId());
pstmt.setInt(2, account.getMoney());
pstmt.setString(3, account.getName());
n=pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUitl.close(pstmt, null);
}
return n;
}
public Account findById(int id){
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Account account = null;
try {
con = JdbcUitl.getConn();

pstmt=con.prepareStatement("select * from account where id="+id);
rs = pstmt.executeQuery();
if(rs.next()){
account = new Account();
account.setId(rs.getInt("id"));
account.setMoney(rs.getInt("money"));
account.setName(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUitl.close(pstmt, rs);

}
return account;
}

public List<Account> getllAccount(){
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Account> accounts = new ArrayList<Account>();
try {
con = JdbcUitl.getConn();
pstmt = con.prepareStatement("select id,money,name from account");
rs = pstmt.executeQuery();
while(rs.next()){
Account account = new Account();
account.setId(rs.getInt("id"));
account.setMoney(rs.getInt("money"));
account.setName(rs.getString("name"));
accounts.add(account);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUitl.close(pstmt, rs);
}
return accounts;
}
public int updateAccount(int id,int money){
Connection con = null;
PreparedStatement pstmt = null;
int n = 0;
try {
con = JdbcUitl.getConn();
String sql = "update account set money = ? where id=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setInt(2, id);
n=pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUitl.close(pstmt,null);
}
return n;
}
}

测试类

package lesson2;

import java.util.List;

public class JdbcDemo2 {
public static void main(String[] args) {
AccountDao ad = new AccountDao();
//查询全部记录
/* List<Account> accounts = ad.getllAccount();
for (Account account : accounts) {
System.out.println(account);
} */


//修改记录
/* int n = ad.updateAccount(1002, 300);
System.out.println(n);
*/


//根据主键查询
Account account = ad.findById(1001);
System.out.println(account);
}
}
  • 数据层:关于数据库的操作 ,增删改查
  • 业务层:正常的业务处理 比如转账业务(A—>B):修改两次数据操作。也有可能和数据层无关
  • 完成转账操作
    • A转账给B 这个过程A减少钱,若果B出意外了。所以两个操作放在一个事物中,而connection中,默认自动提交。
    • 要在同一个事物中操作,就必须多个关于数据的操作必须是同一个连接,要保证同一个线程中是同一个connection。
    • 修改 JdbcUtil threadLocal
    • Connection 的关闭,必须在一个业务操作完成之后,进行关闭。
  • 业务处理开始之前,开启事物,业务处理之后提交事物,业务处理有异常 回滚事物,左后关闭connection
  • 将来会有,一个业务操作一组dao, 我们就是需要把多个业务包装到一个dao中,要么全成功,要么全全失败、
  • 但是每个业务都这样写,比较繁琐。我们希望利用代理的方式。

JdbcUitl类

保证同一个线程中是同一个connection。方面后面的事务操作

package lesson2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JdbcUitl {
private static Properties prop = new Properties();
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
static{
try {
prop.load(JdbcUitl.class.getResourceAsStream("/database.properties"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConn(){
//保证同一个线程中是同一个connection
Connection con = tl.get();
try {
if(con==null){
Class.forName(prop.getProperty("driverClass"));
con = DriverManager.getConnection(
prop.getProperty("url"),prop.getProperty("user"),prop.getProperty("pass"));
tl.set(con);
}
return tl.get();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 资源关闭
* @param con
* @param stmt
* @param rs
*/

public static void close(Statement stmt,ResultSet rs){
if(rs!=null)
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void closeConnection(){
Connection con = tl.get();
if(con!=null){
try {
con.close();
tl.remove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

业务类

手动抛出异常,测试可以发现,用之前的操作方法,如果两个操作之间发生异常,则会造成数据不一致。

package lesson2;

import java.sql.Connection;
import java.sql.JDBCType;
import java.sql.SQLException;

public class AccountService {
private boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
AccountDao ad = new AccountDao(); //未来用别的模式初始化(工厂模式)


/**
* 增加账户的业务
* @param account
*/

public void addAccount(Account account){
//如果有密码:需要加密 然后增加一条记录
}


/**
* 转账业务
* @param id1
* @param id2
* @param money
*/

public void changeAccount(int id1,int id2,int money){
Connection con = null;
try {
con = JdbcUitl.getConn();
//开启事务,关闭自动提交
con.setAutoCommit(false);
Account account1 = ad.findById(id1);
Account account2 = ad.findById(id2);
int n1=ad.updateAccount(id1,account1.getMoney()-money);
if(flag)
throw new RuntimeException("可能出现某种异常");
int n2=ad.updateAccount(id2,account2.getMoney()+ money);
//提交事务
con.commit();
} catch (Exception e) {
e.printStackTrace();
try {
//回滚事务
con.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}finally{
//关闭事务
JdbcUitl.closeConnection();
}
}


}

测试类

package lesson2;
/**
* 事物测试类
* @author hh
*
*/

public class JdbcDemo3 {
public static void main(String[] args) {
AccountService service = new AccountService();
service.setFlag(true);
service.changeAccount(1001, 1002, 100);
}
}

代理模式

  • 代理模式(基于接口编程,业务要写成接口。所有操作,无论DAO还是业务都应该写成接口)。面向方面的编程aop。关心业务的人员只关心业务,关心事物的人员只关心事物。
    • 静态代理
      • 创建一个代理类,和被代理类实现用一个接口,并且把被代理类的实例作为自己的成员。
      • 静态代理本身也是一种架构模式
      • 缺陷:要做的辅助操作(实务,日志)在每个业务方法中都要进行重写。
    • 动态代理
      • 动态代理处理器 实现InvokeHandler接口
      • 把目标对象作为成员(要生成代理的对象)
      • 重写invoke方法:该方法会拦截所有的目标对象的方法
      • 产生代理对象,可以直接利用代理器处理器类,可单独写一个类;
      • 缺点:目标对象的所有方法都会被拦截处理。将来可以根据方法对象的属性判断是否需要加入事务操作。

静态代理

创建服务业务的接口

package lesson2.proxy;

public interface UserService {
public void inertUser();
public void updateUser();
public void deleteUser();
}

实际业务实现接口

package lesson2.proxy;
public class UserServiceImpl implements UserService{
@Override
public void inertUser() {
// System.out.println("开启事务");
System.out.println("通过一个or多个dao操作完成增加用户的业务");
// System.out.println("提交事务,如果有异常就需要回滚事务");
}
@Override
public void updateUser() {
// System.out.println("开启事务");
System.out.println("通过一个or多个dao操作完成修改用户的业务");
// System.out.println("提交事务,如果有异常就需要回滚事务");
}
@Override
public void deleteUser() {
System.out.println("通过一个or多个dao操作完成删除用户的业务");
}
}

代理实现接口

package lesson2.proxy;

public class UserServiceProxy implements UserService{
private UserService userService;

public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void inertUser() {
System.out.println("开启事务,日志,权限~~~~");
userService.inertUser();
System.out.println("提交事务,如果有异常就需要回滚事务");
}
@Override
public void updateUser() {
System.out.println("开启事务");
userService.updateUser();
System.out.println("提交事务,如果有异常就需要回滚事务");
}
@Override
public void deleteUser() {
System.out.println("开启事务");
userService.deleteUser();
System.out.println("提交事务,如果有异常就需要回滚事务");
}
}

测试类

package lesson2.proxy;
/**
* 静态代理测试
* @author hh
*
*/

public class ProxyDemo1 {
public static void main(String[] args) {
UserServiceImpl us = new UserServiceImpl();
UserServiceProxy uproxy=new UserServiceProxy();
uproxy.setUserService(us);
// us.updateUser();
// us.inertUser();
uproxy.updateUser();
uproxy.inertUser();
}
}

动态代理

可以发现静态代理中,要做的辅助操作(实务,日志)在每个业务方法中都要进行重写,繁琐。

代理处理器 代理工厂

package lesson2.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;



public class ProxyFactoryBean implements InvocationHandler {
private Object target;// 目标对象 --->调用目标对象的任何方法都会被invoke方法拦截。

public ProxyFactoryBean(Object target) {
this.target = target;
}

/**
* 参数method 方法拦截后,拦截的目标对象的方法。 args 就是方法的参数。
*/

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//可以根据方法对象的特点来判断是否需要加入事务操作
System.out.println("开启事务 日志 权限等操纵");
method.invoke(target, args); // 目标方法的调用
System.out.println("提交事务,如果有异常就需要回滚事务");
return null;
}

/**
* 第一个参数:目标对象的类加载器
* 第二个参数:对象所实现的接口
* 第三个参数:代理处理器类
* @return
*/

public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);

}
}

测试类

package lesson2.proxy;
/**
* 动态代理测试
* @author hh
*
*/

public class ProxyDemo2 {
public static void main(String[] args) {
UserServiceImpl us = new UserServiceImpl(); //目标对象
//代理处理器、代理工厂
ProxyFactoryBean pfb= new ProxyFactoryBean(us);
//产生代理对象
UserService usProxy = (UserService)pfb.getProxy();
usProxy.deleteUser();
usProxy.updateUser();
usProxy.inertUser();
}
}

用代理模式完成操作

  • 转账业务
  • 添加业务
  • 查询业务(不需要启动业务) 通过注解实现 是否需要事务 注解要加在接口上提炼事务的操作
  • 事务的提交回滚包装到专门的类中

创建业务接口

package lesson2.proxyaccount;
import java.util.List;
import lesson2.Account;
public interface AccountService {

/**
* 增加账户
* @param account
* @return
*/

public int addAccount(Account account);


/**
* 转账
* @param id1
* @param id2
* @param money
*/

public void changeAccount(int id1,int id2,int money);


/**
* 查询所有
* @return
*/

@Support
public List<Account> getAll();

}

实际业务操作(实现接口方法)

package lesson2.proxyaccount;

import java.util.List;

import lesson2.Account;
import lesson2.AccountDao;
import lesson2.UserDao;

public class AccountServiceImpl implements AccountService{
private AccountDao adDao = new AccountDao();
private boolean flag;

@Override
public int addAccount(Account account) {
int n =adDao.addAccount(account);
return n;
}

@Override
public void changeAccount(int id1, int id2, int money) {
Account account1 = adDao.findById(id1);
Account account2 = adDao.findById(id2);
//省略用户不存在或者账户余额不足的操作。
int n1=adDao.updateAccount(id1,account1.getMoney()-money);
if(flag){ //模拟出错
throw new RuntimeException("未知错误");
}
int n2=adDao.updateAccount(id2,account2.getMoney()+ money);

}

@Override
@Support
public List<Account> getAll() {
List<Account> accounts =adDao.getllAccount();
return accounts;
}

}

动态代理操作(实现接口)

package lesson2.proxyaccount;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

import lesson2.JdbcUitl;

public class ProxyFactoryBean implements InvocationHandler {
private Object target;

public ProxyFactoryBean(Object target) {
super();
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] arg) {
Support support = method.getAnnotation(Support.class);
if(support!=null&& support.value().equals("none")){
try {
proxy= method.invoke(target,arg);
System.out.println("没有事务");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
JdbcUitl.closeConnection();
}
return proxy;
}
return proxyMerhod(proxy, method, arg);
}

private Object proxyMerhod(Object proxy, Method method, Object[] arg) {
// Connection con = JdbcUitl.getConn();
try {
// 开始事务
// con.setAutoCommit(false);
TransactionManager.begin();
proxy=method.invoke(target, arg); // 业务调用
// con.commit(); // 提交事物
TransactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
try {
// con.rollback(); // 业务回滚
TransactionManager.rollback();
} catch (SQLException e1) {

e1.printStackTrace();
} finally {
JdbcUitl.closeConnection(); // 业务关闭
}
}
return proxy;
}

public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
}

事务处理(开始,提交,回滚)

package lesson2.proxyaccount;

import java.sql.Connection;
import java.sql.SQLException;

import lesson2.JdbcUitl;

public class TransactionManager {
public static void begin()throws SQLException{
Connection con =JdbcUitl.getConn();
con.setAutoCommit(false);
}
public static void commit()throws SQLException{
Connection con =JdbcUitl.getConn();
con.commit();
}
public static void rollback()throws SQLException{
Connection con =JdbcUitl.getConn();
con.rollback();
}
}

用标签修饰方法

决定是否需要进行事务操作。例如查询所有就无需事务操作

package lesson2.proxyaccount;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)

public @interface Support {
String value()default "none";
}

标签要修饰在接口上

    /**
* 查询所有
* @return
*/

@Support
public List<Account> getAll();

测试类

package lesson2.proxyaccount;
import java.util.List;
import lesson2.Account;

public class AccountTest {
public static void main(String[] args) {
//未产生代理
// AccountServiceImpl accountService = new AccountServiceImpl();
// accountService.changeAccount(1001, 1002, 100);

//产生代理
AccountServiceImpl accountService = new AccountServiceImpl();
ProxyFactoryBean pfb = new ProxyFactoryBean(accountService);
AccountService accountServiceProxy = (AccountService)pfb.getProxy();

//测试转账
accountServiceProxy.changeAccount(1001, 1002, 100);
//测试增加一条记录
// Account account = new Account(1011, 100,"zhangsan2");
// accountServiceProxy.addAccount(account);
//全部查询
List<Account> accounts = accountServiceProxy.getAll();
for (Account account : accounts) {
System.out.println(account);
}
}
}

写在最后

这是jdbc学习的第二篇笔记。

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告