目前最好的关系型数据库。
基本的CRUD命令
SQL语句。select(R),update(U),delete(D),insert(C)
中小型项目非常好用的关系型数据库。
灵活,小巧。
ER图---》根据需求分析数据库结构。
三范式---》学会使用范式来规范表结构
在Java中连接数据库技术。
在Java代码中想操作数据库中的数据,就必须使用到JDBC技术。
提供JDBC从Java代码中想数据库发送执行的SQL语句。
工具类的封装。DAO-Service 的分层。
一定要遵守编码规范。
就是一套使用的规范。大家按规范编写就没有什么特别难理解的部分。
难点:
a 要使用连接池技术
b ThreadLocal类。理解上需要大家想。
a 按规范比那些代码。
b 练习
JDBC : Java DataBase Connector 中文:Java数据库连接技术
JDBC 是一种结束。一种在Java程序中连接数据库的技术。
使用JDBC可以实现在Java代码中去操作数据库表中的记录。
JDBC是一个持久化技术
化:过程。
数据是分为零食状态和持久状态。
临时状态:数据保存在内存中。代码在运行时的数据。
持久状态:数据保存在硬盘中,以文件的方式长期保存的数据。
持久化:数据在临时状态 与 持久状态 之间进行切换的过程。
1 将Java程序中的数据保存到数据库中。
2 将数据库职工的数据读取到Java程序中
JDBC是只一套规范,约定了Java语言连接到不同数据库产品时的标准。
例如:USB接口。
任何数据库产品,想让自己的产品,可以使用JDBC连接到Java程序,就要按JDBC的标准编写数据库的驱动。
数据库的驱动是数据库厂商按JDBC标准,由各数据库厂商自己编写的。
在Java程序中接口表示了一种能力,一种规范,一个标准,一种约定。
JDBC本身就是一组接口。各大数据库厂商需要根据自己的数据库去实现这一组接口。
Connection是连接对象,负责创建Java程序与数据库之间的连接。
Connection接口的实现类由各数据库厂商提供。称为驱动类。操作数据库之前必先获得驱动类。
常用的子接口PreparedStatement接口。
PreparedStatement是操作对象。负责通过Connection的连接,向数据库执行SQL语句。
ResultSet是结果集接口,当我们使用PreparedStatement向数据库执行Select命令时,Java程序需要能接收查询结果。
在Java程序端就是使用ResultSet对象来接收查询结果的。
数据库的查询结果就是一个二维表格。
ResultSet也是一个表格的处理方式。
使用JDBC操作数据库的步骤就三步:
1 打开连接
将Java程序与数据库创建连接。
2 操作数据库
通过Java程序向数据库发送SQL语句执行。
分增删改,和 查询。
查询有返回值,返回值叫结果集。
3 关闭连接
释放资源,一定要执行的部分。finally块。
打开连接,先获取连接数据库的驱动类。
驱动类是数据库厂商提供,是个Jar包。
还要再确定四个参数:
1 驱动类的名称
2 连接数据库的字符串
3 数据库账号
4 密码
//四大参数
String driverClassName="oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:XE";
String userName = "no5";
String userPass = "123456";
//获得连接对象
Connection conn = Connection(url,userName,userPass);
//通过PreparedStatement对象来操作数据库
String sql = "insert into users_no5(user_id,user_name) values( val , '都醒着')";
PreparedStatement pstat = conn.prepareStatement(sql);
uteUpdate();//执行增删改语句的方法
rs对象就是一个指向二维表格的引用。rs对象每次只能指向一行。通过next()向下移动一行。
通过getXXX()方法获取指定列的值。
//通过PreparedStatement对象来操作数据库
String sql = "select * from users_no5";
PreparedStatement pstat = conn.prepareStatement(sql);
ResultSet rs = pstat.executeQuery();//rs就是一个二维表格。
while(rs.next()){//向下移一行。
System.out.println(rs.getInt("user_id"));
System.out.println(rs.getString("user_name"));
System.out.println("----------");
}
//关闭连接
conn.close();
使用PreparedStatement可以提前将要执行的SQL语句进行数据库端的预编译。
使用PreparedStatement可以提高执行效率。并安全。
在创建pstat对象时,就以及提前将SQL写好了。
String sql = "insert into users_no5(user_id,user_name) val,'XXX')" ;
PreparedStatement pstat = conn.prepareStatement(sql);
String sql = "insert into users_no5(user_id,user_name) val,?)" ;
PreparedStatement pstat = conn.prepareStatement(sql);
一定是在SQL语句执行之前,先为所有的?号进行赋值。
public abstract void setString(int parameterIndex,String x)
参数:parameterIndex 表示?的索引。从1开始。
x 表示值。
String sql = "insert into users_no5(user_id,user_name) val,?)" ;
PreparedStatement pstat = conn.prepareStatement(sql);
pstat.setString(1,"???");
int i = uteUpdate();
1 PreparedStatement因为是预编译的原因,可以使用占位符?,这样在之后可以调用set方法来灵活改变SQL语句的内容。
2 PreparedStatement的在执行一次是的开销要大于Statement,但是面对一次性操作也不会带来更多的好处。
3 在大量对象的情况下,由于PreparedStatement是预编译,所以在执行时效率更高。
ResultSet对象是用来接收查询结果的对象。
数据库的查询结果就是一个二维表格。
rs对象就是一个指向查询结果每一行的一个指针。
通过pstat对象执行 executeQuery()方法获得一个RS对象。
String sql = "select * from users_no5 order by user_id ";
PreparedStatement pstat = conn.prepareStatement(sql);
ResultSet rs = uteQuery();
BOF表示第一行的上面。
EOF表示最后一行的下面。
rs对象就是一个指向查询结果每一行的一个指针。
查询结果能不能没有记录?可以。
所以rs对象默认指向BOF。只有调用一次next()方法才能指向第一行。
如果rs对象调用next()方法,指向了EOF,就表示当前查询结果已经没有了。next()方法返回false;
boolean next() throws SQLException;
向一移动一行。当指向EOF时返回false。
ResultSet rs = uteQuery();
()){int userId = rs.getInt("user_id");String name = rs.getString("user_name");System.out.println(userId+":"+name);System.out.println("----------");
}
String getString(String columnLabel) throws SQLException;
int getInt(String columnLabel) throws SQLException;
封装原则:将变化的 和 不变化的 分开进行封装。
以实现,在需求改时,对变化的部分进行修改时,不会影响到不变化 的部分。
数据库的操作步骤:3步。
1 打开连接
2 操作数据库
3 关闭连接
变化 :2 操作数据库 (DAO 数据访问对象)
这部分内容是根据操作的不同,代码不一致。
针对不同的表。针对同一张表的不同的CRUD。
不变化 :1 打开连接 和 3 关闭连接 (工具类)
不管对数据库进行什么操作, 都必须先打开连接,最后关闭连接。
在这个工具类中,要封装二个行为。
1 打开连接
3 关闭连接
/*** 在这个工具类中,要封装二个行为。* 1 打开连接 getConn()* 3 关闭连接 closeConn()*/
public class ConnUtils {private static Connection conn;private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";//SID数据库实例名private static final String USER_NAME = "no5";private static final String USER_PASS = "123456";private static final String CLASS_NAME = "oracle.jdbc.driver.OracleDriver";/*** 打开连接** @return 连接对象*/public static Connection getConn() throws SQLException {if (conn == null || conn.isClosed()) {conn = Connection(URL, USER_NAME, USER_PASS);}return conn;}/*** 关闭连接*/public static void closeConn() throws SQLException {try {if (conn != null && !conn.isClosed()) {conn.close();}} finally {conn = null;}}
}
数据库表:users_no5
表示的是用户信息。
表中的每一第记录就是一个用户实体。
表结构:
????转换到Java程序来。
用户实体 在Java程序中使用什么表示?
//users_no5
public class Users {// USER_ID NUMBER(7,0)private Integer userId;// USER_NAME VARCHAR2(20 BYTE)private String userName;
}
public static void main(String[] args) {Users user = new Users();user.setUserId(1);user.setUserName("张三");System.out.println(user);
}
DAO:数据访问对象。负责封装操作数据库的代码。
案例JdbcTest03:向Users_no5插入一条记录
案例JdbcTest04:查询Users_no5所有记录
需求在UsersDAO类中编写二个方法,封装以上二个案例的内容。
封装的方法原型:
案例JdbcTest03:public void insert(Users user);
案例JdbcTest04:public List<Users> selectAll();
public class UsersDAO {public void update(Users user) throws SQLException {String sql = "update users_no5 set user_name = ? where user_id = ?" ;Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setString(UserName());pstat.setInt(UserId());uteUpdate();}public void delete(Integer userId) throws SQLException {String sql = "delete from users_no5 where user_id = ?" ;Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setInt(1,userId);uteUpdate();}public void insert(Users user) throws SQLException {String sql = "insert into users_no5(user_id,user_name) val,?)" ;Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setString(UserName());uteUpdate();}/*** 按主键查询用户对象的方法* @param userId 用户编号* @return 用户对象,如果没有找到返回null* @throws SQLException*/public Users selectById(Integer userId) throws SQLException {String sql = "select * from users_no5 where user_id = ?";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setInt(1,userId);ResultSet rs = uteQuery();()){Users user = new Users();user.Int("user_id"));user.String("user_name"));return user;}else{return null;}}public List<Users> selectAll() throws SQLException {List<Users> usersList = new ArrayList<>();String sql = "select * from users_no5 order by user_id";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);//查询操作一定要接收查询结果ResultSet rs = uteQuery();()){Users user = new Users();user.Int("user_id"));user.String("user_name"));usersList.add(user);}return usersList;}
}
public class UsersDAOTest {public static void main(String[] args) throws SQLException {Users user = new Users();user.setUserName("张光明");UsersDAO usersDAO = new UsersDAO();usersDAO.insert(user);List<Users> usersList = usersDAO.selectAll();System.out.println(usersList);System.out.println("end!");}
}
Junit是一个单元测试的软件。
我们编写的类中方法,使用Junit可以方便的进行单元测试。
public class UsersDAOTest {
@Test
public void selectAll() throws SQLException {
UsersDAO usersDAO = new UsersDAO();
List<Users> usersList = usersDAO.selectAll();
System.out.println(usersList);
}
@Test
public void insert() throws SQLException {
Users user = new Users();
user.setUserName("胡伟");
UsersDAO usersDAO = new UsersDAO();
usersDAO.insert(user);
}
@Test
public void update() {
try {
UsersDAO usersDAO = new UsersDAO();
Users user = new Users();
user.setUserId(4);
user.setUserName("巩鹏程");
usersDAO.update(user);
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
ConnUtils.closeConn();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public class ConnUtils {private static Connection conn;private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE"; private static final String USER_NAME = "no5";private static final String USER_PASS = "123456";private static final String CLASS_NAME = "oracle.jdbc.driver.OracleDriver";public static Connection getConn() throws SQLException {if (conn == null || conn.isClosed()) {conn = Connection(URL, USER_NAME, USER_PASS);}return conn;}public static void closeConn() throws SQLException {try {if (conn != null && !conn.isClosed()) {conn.close();}} finally {conn = null;}}
}
第一版在单用户使用时,没有问题。
我们后面一定会学习web应用程序开发的。像taobao,jd这些是web应用程序?
Web应用程序本身就是一个多用户的应用程序。每一个用户是一个单独的线程。
private static Connection conn;
static 静态,属于类的属性,整个应用程序只有一份。
单用户应用时,只用一个连接对象没关系。
多用户应用时,就变成所有用户使用同一个连接对象。
要求:操作数据库一共有3个步骤。第3步是什么?关闭连接。
本地线程容器对象。每一个线程都有一个自己的ThreadLocal对象。
这个对象只能保存一个内容。
public class ConnUtils {private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";private static final String USER_NAME = "no5";private static final String USER_PASS = "123456";private static final String CLASS_NAME = "oracle.jdbc.driver.OracleDriver";/*** 打开连接** @return 连接对象*/public static Connection getConn() throws SQLException {Connection conn = ();if (conn == null || conn.isClosed()) {conn = Connection(URL, USER_NAME, USER_PASS);THREAD_LOCAL.set(conn);}return conn;}/*** 关闭连接*/public static void closeConn() throws SQLException {try {Connection conn = ();if (conn != null && !conn.isClosed()) {conn.close();}} finally {THREAD_LOCAL.set(null);}}
}
//Goods_no5
public class Goods {// GOODS_ID NUMBER(7,0)private Integer goodsId;// GOODS_NAME VARCHAR2(200 BYTE)private String goodsName;// GOODS_PRICE NUMBER(7,2)private Double goodsPrice;// GOODS_DESC VARCHAR2(2000 BYTE)private String goodsDesc;// GOODS_TYPE_ID NUMBER(7,0)private Integer goodsTypeId;
public List<Goods> selectByName(String goodsName) throws SQLException {List<Goods> goodsList = new ArrayList<>();String sql = "select * from goods_no5 where goods_name like ?";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setString(1,"%"+goodsName+"%");ResultSet rs = uteQuery();()){Goods goods = new Goods();goods.Int("GOODS_ID"));goods.String("GOODS_NAME"));goods.Double("GOODS_PRICE"));goods.String("GOODS_DESC"));goods.Int("GOODS_TYPE_ID"));goodsList.add(goods);}return goodsList;
}
测试类:
public class GoodsDAOTest {private GoodsDAO goodsDAO = new GoodsDAO();@Testpublic void selectByName() throws SQLException {System.out.println(goodsDAO.selectByName("小米"));}
}
public List<Goods> selectByPrice(Double min,Double max);
public List<Goods> selectByPrice(Double min,Double max) throws SQLException {List<Goods> goodsList = new ArrayList<>();String sql = "select * from goods_no5 where goods_price between ? and ?";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setDouble(1,min);pstat.setDouble(2,max);ResultSet rs = uteQuery();()){Goods goods = new Goods();goods.Int("GOODS_ID"));goods.String("GOODS_NAME"));goods.Double("GOODS_PRICE"));goods.String("GOODS_DESC"));goods.Int("GOODS_TYPE_ID"));goodsList.add(goods);}return goodsList;
}
在数据库中有一对一,一对多,多对一,多对多。
针对每一张表,就只有两个映射基数。(一 和 多)
在Java中,一对多和多对一不是一个事。
表被映射为一个类。
类与类之间如何表示数据库中的映射基数,
一 : 对象
多 : 集合
本次我们九先不去讨论 自连接
实体类:
//type_no5表
public class Types {// TYPE_ID NUMBER(7,0)private Integer typeId;// TYPE_NAME VARCHAR2(20 BYTE)private String typeName;// TYPE_PID NUMBER(7,0)private Integer typePid;// TYPE_LEVEL NUMBER(7,0)private Integer typeLevel;// TYPE_PATH VARCHAR2(2000 BYTE)private String typePath;
在数据库中整理一下:就二种: X对一,X对多。
X对一 : 在实体类中就是一个 对象。
X对多 : 在实体类中就是一个 集合。
使用实体类表示 商品 与 类型 的关系 完整结构如下:
//Goods_no5
public class Goods {// GOODS_ID NUMBER(7,0)private Integer goodsId;// GOODS_NAME VARCHAR2(200 BYTE)private String goodsName;// GOODS_PRICE NUMBER(7,2)private Double goodsPrice;// GOODS_DESC VARCHAR2(2000 BYTE)private String goodsDesc;// GOODS_TYPE_ID NUMBER(7,0)private Types goodsType;//商品属于哪一个类型。多对一
//type_no5表
public class Types {// TYPE_ID NUMBER(7,0)private Integer typeId;// TYPE_NAME VARCHAR2(20 BYTE)private String typeName;// TYPE_PID NUMBER(7,0)private Integer typePid;// TYPE_LEVEL NUMBER(7,0)private Integer typeLevel;// TYPE_PATH VARCHAR2(2000 BYTE)private String typePath;// 类型有哪些商品? 一对多。private List<Goods> goodsList;
必须使用连接查询,才能在结果集中获取到商品表与类型表 , 二张表的字段。
select * from goods_no5 left join type_no5 on goods_type_id = type_id
public List<Goods> selectByName(String goodsName) throws SQLException {List<Goods> goodsList = new ArrayList<>();String sql = "select * from goods_no5 " +" left join type_no5 on goods_type_id = type_id " +" where goods_name like ? ";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setString(1,"%"+goodsName+"%");ResultSet rs = uteQuery();()){Goods goods = new Goods();Types goodsType = new Types();goods.Int("GOODS_ID"));goods.String("GOODS_NAME"));goods.Double("GOODS_PRICE"));goods.String("GOODS_DESC"));goodsType.Int("GOODS_TYPE_ID"));goodsType.String("TYPE_NAME"));goodsType.Int("TYPE_LEVEL"));goodsType.String("TYPE_PATH"));goodsType.Int("TYPE_PID"));goods.setGoodsType(goodsType);//维护关系goodsList.add(goods);}return goodsList;
}
保存一个商品。
/*** 向商品表中插入记录的方法* @param goods 商品对象* @throws SQLException*/
public void insert(Goods goods) throws SQLException {String sql = "insert into " +" goods_no5(GOODS_ID,GOODS_NAME,GOODS_PRICE,GOODS_DESC,GOODS_TYPE_ID) " +" val,?,?,?,?)";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setString(GoodsName());pstat.setDouble(GoodsPrice());pstat.setString(GoodsDesc());pstat.setInt(GoodsType().getTypeId());uteUpdate();
}
测试类:
public class GoodsDAOTest {private GoodsDAO goodsDAO = new GoodsDAO();@Testpublic void insert() throws SQLException {Types goodsType = new Types();goodsType.setTypeId(2);//在数据库中维护关系只靠主键。Goods goods = new Goods();goods.setGoodsDesc("小心别买错!");goods.setGoodsName("雷碧");goods.setGoodsPrice(3.0);goods.setGoodsType(goodsType);goodsDAO.insert(goods);}
例如:增加商品时,商品名不可以重复。
例如:修改数量时,数量不能小于0。
在我们编写的Service类中,要处理如下三件事:
1 异常处理
2 事务处理
3 资源释放(连接关闭)
事务是数据库技术中的一个特性。
事务表示是一组不可分割的整体操作。
事务正常操作就全体执行。否则就全体不执行。
例如:转账是一个事务。
完整的转账操作有二步:
1 A+1000
2 B-1000
转账的二个操作就应该是一个事务整体。
在数据库中操作事务三个动作:
1 打开事务
2 以提交方式关闭事务 确定事务过程中的修改
3 以回滚方式关闭事务 撤消事务过程中的修改
第一点:同一个事务必须使用相同的Connection对象。
第二点:在JDBC中事务操作共三个方法。
1 打开事务 conn.setAutoCommit(false);//关闭自动提交事务
2 提交事务 connmit();
3 回滚事务
conn.setAutoCommit(false); 在JDBC中每一次数据库操作都默认是一个事务,会进行自动事务提交。
第三点:在JDBC中查询默认不需要事务。增删改必须写事务。
public class GoodsService {private GoodsDAO goodsDAO = new GoodsDAO();}
public class GoodsService {private GoodsDAO goodsDAO = new GoodsDAO();public List<Goods> searchAll() {try {dsDAO.selectAll();} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);} finally {try {ConnUtils.closeConn();} catch (SQLException e) {e.printStackTrace();}}}}
删除方法,以后在实际开发中没事别用。
public class GoodsService {private GoodsDAO goodsDAO = new GoodsDAO();public void deleteById(Integer goodsId){try {Conn().setAutoCommit(false);dsDAO.deleteById(goodsId);Conn()mit();} catch (Exception e) {e.printStackTrace();try {Conn().rollback();} catch (SQLException ex) {ex.printStackTrace();}throw new RuntimeException(e);} finally {try {ConnUtils.closeConn();} catch (SQLException e) {e.printStackTrace();}}}
}
增加商品有业务规则:相同的商品名称不能增加。
如何判断一个商品名称是不是重复?商品名称在数据库中存在不存在?
使用商品名称去查询数据库。
public Goods selectByGoodsName(String goodsName);
/*** 按商品名称查询商品对象的方法* @param goodsName 商品名称* @return 商品对象,如果没有找到返回null* @throws SQLException*/
public Goods selectByGoodsName(String goodsName) throws SQLException {String sql = "select * from goods_no5 " +" left join type_no5 on goods_type_id = type_id " +" where goods_name = ? ";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setString(1,goodsName );ResultSet rs = uteQuery();()){Goods goods = new Goods();Types goodsType = new Types();goods.Int("GOODS_ID"));goods.String("GOODS_NAME"));goods.Double("GOODS_PRICE"));goods.String("GOODS_DESC"));goods.setGoodsType(goodsType);//维护关系goodsType.Int("GOODS_TYPE_ID"));goodsType.String("TYPE_NAME"));goodsType.Int("TYPE_LEVEL"));goodsType.String("TYPE_PATH"));goodsType.Int("TYPE_PID"));return goods;}else{return null;}
}
业务有例外:当商品名称重复时不能增加。
public class NameException extends Exception{public NameException(String message) {super(message);}
}
public void save(Goods goods) throws NameException;
public class GoodsService {private GoodsDAO goodsDAO = new GoodsDAO();/*** 保存新商品的方法* @param goods 新商品对象* @throws NameException 当商品名称已经存在时,抛出异常。*/public void save(Goods goods) throws NameException {try {if (dsDAO.GoodsName()) == null) {Conn().setAutoCommit(false);dsDAO.insert(goods);Conn()mit();} else {throw new NameException("商品名称已经存在!");}} catch (NameException e) {e.printStackTrace();throw e;} catch (Exception e) {e.printStackTrace();try {Conn().rollback();} catch (SQLException ex) {ex.printStackTrace();}throw new RuntimeException(e);} finally {try {ConnUtils.closeConn();} catch (SQLException e) {e.printStackTrace();}}}
}
public class GoodsServiceTest {private GoodsService goodsService = new GoodsService();@Testpublic void save(){Goods goods = new Goods();goods.setGoodsPrice(5000.0);goods.setGoodsName("入门机单反");goods.setGoodsDesc("除了贵点没什么优点!");Types goodsType = new Types();goodsType.setTypeId(4);goods.setGoodsType(goodsType);try {goodsService.save(goods);System.out.println("商品保存成功!");} catch (NameException e) {e.printStackTrace();System.out.Message()+"保存失败!请重新输入!");}}
}
基本上在开发时,我们大部分时间处理的关系都是多对一。
在数据库中维护关系只需要依靠 外键 字段。
商品表(goods_no5)与 类型表(type_no5)之间的关系依赖 goods_type_id。
两类可以分别描述关系的
//Goods_no5
public class Goods {
// GOODS_ID NUMBER(7,0)
private Integer goodsId;
// GOODS_NAME VARCHAR2(200 BYTE)
private String goodsName;
// GOODS_PRICE NUMBER(7,2)
private Double goodsPrice;
// GOODS_DESC VARCHAR2(2000 BYTE)
private String goodsDesc;
// GOODS_TYPE_ID NUMBER(7,0)
private Types goodsType;//商品属于哪一个类型。多对一
//type_no5表
public class Types {
// TYPE_ID NUMBER(7,0)
private Integer typeId;
// TYPE_NAME VARCHAR2(20 BYTE)
private String typeName;
// TYPE_PID NUMBER(7,0)
private Integer typePid;
// TYPE_LEVEL NUMBER(7,0)
private Integer typeLevel;
// TYPE_PATH VARCHAR2(2000 BYTE)
private String typePath;
// 一个类型下的多个商品,一对多关系。
private List<Goods> goodsList;
因为多对一时,本身的每一条记录,只能连接出一条记录。
在记录数量上不会发生改变的。
String sql = "select * from goods_no5 " +" left join type_no5 on goods_type_id = type_id ";
因为一对多时,本身的每一条记录,有可能连接出多条记录。
在记录数量上会发生改变的。
所以我们需要大家编写二个DAO的方法。
select * from type_no5 where type_id = 2 ;
select * from goods_no5 where goods_type_id = 2;
在service层完成关系的维护
按类型编号查询类型对象的方法
public Types selectById(Integer typeId);
public class TypeDAO {public Types selectById(Integer typeId) throws SQLException {String sql = "select * from type_no5 where type_id = ? ";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setInt(1,typeId);ResultSet rs = uteQuery();()){Types type = new Types();type.Int("TYPE_ID"));type.String("TYPE_NAME"));type.Int("TYPE_PID"));type.Int("TYPE_LEVEL"));type.String("TYPE_PATH"));return type;}else{return null;}}
}
按类型编号查询商品集合的方法
public List<Goods> selectByType(Integer typeId);
public List<Goods> selectByType(Integer typeId) throws SQLException {List<Goods> goodsList = new ArrayList<>();String sql = "select * from goods_no5 " +" left join type_no5 on goods_type_id = type_id " +" where goods_type_id = ?";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);pstat.setInt(1,typeId);ResultSet rs = uteQuery();()){Goods goods = new Goods();Types goodsType = new Types();goods.Int("GOODS_ID"));goods.String("GOODS_NAME"));goods.Double("GOODS_PRICE"));goods.String("GOODS_DESC"));goods.setGoodsType(goodsType);//维护关系goodsType.Int("GOODS_TYPE_ID"));goodsType.String("TYPE_NAME"));goodsType.Int("TYPE_LEVEL"));goodsType.String("TYPE_PATH"));goodsType.Int("TYPE_PID"));goodsList.add(goods);}return goodsList;
}
按类型编号查询类型对象的方法
public Types searchById(Integer typeId);
在这个方法中,我们需要调用TypeDAO. selectById()方法,和 GoodsDAO. selectByType()方法。
完成关系的维护。
public class TypeService {private TypeDAO typeDAO = new TypeDAO();private GoodsDAO goodsDAO = new GoodsDAO();/*** 按类型编号查询类型对象的方法* @param typeId 类型编号* @return 类型对象,并绑定了类型下的商品集合。如果没有找到返回null*/public Types searchById(Integer typeId){try {Types type = peDAO.selectById(typeId);if(type!=null){List<Goods> goodsList = dsDAO.selectByType(typeId);type.setGoodsList(goodsList);//在service中完成关系的维护}return type;} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);} finally{try {ConnUtils.closeConn();} catch (SQLException e) {e.printStackTrace();}}}
}
测试类:
public class TypeServiceTest {private TypeService typeService = new TypeService();@Testpublic void searchById(){int typeId = 2;Types type = peService.searchById(typeId);System.out.println(type);System.out.GoodsList());}
}
第二版中,想要获得一个连接对象,就是需要新创建一个。当一个业务方法完成之后,我们要关闭连接对象。
但是,在整个操作数据库的过程,创建连接和关闭连接是最浪费资源的操作。
解决思路:如果能不用每次都关闭连接。大家能共用同一个连接。可以很好的提高效率。
池:容器。
连接池:一个保存大量连接的容器。
连接池的作用是什么?
当我们需要去操作数据库时,可以从连接池中获得一个连接对象,进行数据库操作,操作完成之后,释放连接对象。
以这种方式使用连接池技术,可以有效的减少创建连接对象的次数。达到提交效率。
市面上主流的连接池技术有什么?C3P0,DBCP,Durid。
java.sql.DataSource对象。称为数据源对象,就是Java程序实现连接池技术的具体对象。
只不过。我们可以使用DBCP等连接池技术来获得数据源对象。
DataSource对象 才是在Java程序中保存大量连接对象的容器。
第一步:导入DBCP的jar文件
第二步:通过一个properties文件配置DBCP连接池
=号左边的关键字,是DBCP连接池使用的。所以一个名字不允许你修改。
driverClassName=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:XE
username=no5
password=123456
maxActive=50
maxIdle=20
maxWait=60000maxActive 最大活动数量,表示连接池中最多有多个连接对象。
maxIdle 最大空闲数量,表示连接池中最多保留几个闲置连接对象。
maxWait 最大等待时长,单位毫秒。
第三步:使用DBCP连接池技术创建DataSource对象
public class DBCPTest {public static void main(String[] args) throws Exception {Properties properties = new Properties();properties.load(ResourceAsStream("/oracle.properties"));//获得数据源对象DataSource ds = ateDataSource(properties);System.out.println(ds);//从数据源中获得一个Connection对象。Connection conn = ds.getConnection();System.out.Class().getName());conn.close();//释放资源}
}
public class ConnUtils {private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();private static DataSource ds = null;static{Properties properties = new Properties();try {properties.load(ResourceAsStream("/oracle.properties"));//获得数据源对象ds = ateDataSource(properties);} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}/*** 打开连接** @return 连接对象*/public static Connection getConn() throws SQLException {Connection conn = ();if (conn == null || conn.isClosed()) {conn = ds.getConnection();THREAD_LOCAL.set(conn);}return conn;}/*** 关闭连接*/public static void closeConn() throws SQLException {try {Connection conn = ();if (conn != null && !conn.isClosed()) {conn.close();}} finally {THREAD_LOCAL.set(null);}}
}
分页查询是现在开发的基础功能。
进行数据展示都需要进行分页查询。
在Oracle中进行分页查询,需要使用到ROWNUM的伪列。
ROWNUM : Oracle数据库中为每一行查询结果编写一个编号的伪列。
RowNum是先为查询到的第一行,加数字1编号,之后再进行where条件的判断。
如果where满足条件就把编号1确定下来。为第二行使用编号2。
如果第一行where不满足条件,就为第二行再次使用编号1。
select rownum rn , goods_no5.* from goods_no5
where rownum >=2 and rownum <= 5;
使用子查询,先确定查询的结果,再为结果增加编号。
使用ROWNUM在Oracle中进行分页查询时,一定是三层子查询嵌套。
select * from (select rownum rn , t.* from (
select * from goods_no5 order by ds_price desc
) t ) temp >=1 <=2
格式:
select * from (select rownum rn , t.* from (
查询的SQL语句
) t ) temp >= A <= B
A= ((页码-1)*每页记录数量)+1
B= 页码*每页记录数量
当进行分页查询时,必须使用到的数据有哪些?
1 page 页码:表示你要查看第几页 是由用户指定
2 recordOfPage 每页记录数量:表示每页查询到的记录条数 由程序员指定
3 pageCount 总页码:将所有数据进行分页时,一共分成了多少页 计算获得
pageCount = ((recordCount-1) / recordOfPage ) + 1
4 recordCount 总记录数量:当前查询语句应该查询到的所有记录的数量 查询获得
select count(*) from 表 where 条件
5 List<T> 当页记录数据:分页时查询到的内容。 分页查询获得
select * from (select rownum rn , t.* from (
Select * from 表 where 条件
) t ) temp >= ((page-1)*recordOfPage)+1 <= (page*recordOfPage)
public class PageVO<T> { |
PageVO<Goods> pageVO = new PageVO<>(); |
PageVO<Types> pageVO = new PageVO<>(); |
public class SqlUtils {/*** 动态获取Oracle分页查询语句* @param sql 查询语句* @param page 当前页码* @param recordOfPage 每页记录数量* @return 封装好的分页查询语句*/public static String getPageSql(String sql,Integer page,Integer recordOfPage){String pageSql = "select * from (select rownum rn , t.* from ( " ;pageSql += sql ;pageSql += " ) t ) temp >= "+ (((page-1)*recordOfPage)+1)+" <= "+(page*recordOfPage) ;return pageSql;}
public Integer getRecordCount();//获取总记录数量
/*** 查询记录总数量的方法* @return 记录总数量* @throws SQLException*/
public Integer getRecordCount() throws SQLException {String sql = "select count(*) from goods_no5";Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(sql);ResultSet rs = uteQuery();rs.next();Int(1);
}
public List<Goods> search(Integer page , Integer recordOfPage);
/*** 分页查询方法* @param page 当前页码* @param recordOfPage 每页记录数量* @return 当前查询的数据*/
public List<Goods> search(Integer page , Integer recordOfPage) throws SQLException {String sql = "select * from goods_no5 order by goods_price desc";String pageSql = PageSql(sql,page,recordOfPage);List<Goods> goodsList = new ArrayList<>();Connection conn = Conn();PreparedStatement pstat = conn.prepareStatement(pageSql);ResultSet rs = uteQuery();()){Goods goods = new Goods();Types goodsType = new Types();goods.Int("GOODS_ID"));goods.String("GOODS_NAME"));goods.Double("GOODS_PRICE"));goods.String("GOODS_DESC"));goods.setGoodsType(goodsType);//维护关系goodsType.Int("GOODS_TYPE_ID"));goodsType.String("TYPE_NAME"));goodsType.Int("TYPE_LEVEL"));goodsType.String("TYPE_PATH"));goodsType.Int("TYPE_PID"));goodsList.add(goods);}return goodsList;
}
测试类:
public class GoodsDAOTest {private GoodsDAO goodsDAO = new GoodsDAO();@Testpublic void search() throws SQLException {List<Goods> goodsList = dsDAO.search(2,2);System.out.println(goodsList);}@Testpublic void getRecordCount() throws SQLException {System.out.RecordCount());}
}
在业务类中分页,需要提交1个数据。页码。
public PageVO<Goods> search(Integer page);
public class GoodsService {private GoodsDAO goodsDAO = new GoodsDAO();/*** 分页查询的业务方法* @param page 当前页码* @return 分页值对象*/public PageVO<Goods> search(Integer page){try {PageVO<Goods> goodsPageVO = new PageVO<>();//定义每页记录数量Integer recordOfPage = 3;//总记录数量Integer recordCount = RecordCount();//总页数Integer pageCount = ((recordCount-1)/recordOfPage) + 1;//当前页码if(page==null) page = 1;if(page < 1) page = 1;if(page > pageCount) page = pageCount;//当前数据List<Goods> goodsList = dsDAO.search(page,recordOfPage);goodsPageVO.setPage(page);goodsPageVO.setPageCount(pageCount);goodsPageVO.setRecordOfPage(recordOfPage);goodsPageVO.setRecordCount(recordCount);goodsPageVO.setList(goodsList);return goodsPageVO;} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);} finally{try {ConnUtils.closeConn();} catch (SQLException e) {e.printStackTrace();}}}
测试类:
public class GoodsServiceTest {private GoodsService goodsService = new GoodsService();@Testpublic void search(){int page = -2;System.out.dsService.search(page));}
}
本文发布于:2024-02-08 20:01:06,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170739388768629.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |