JDBC、mybaties、JPA的数据库开发
一、JDBC
1.概括
1.JDBC是Java访问数据库的标准规范,就是执行sql语句的javaAPI(Java语言通过JDBC可以操作数据库)
2.jdbc的由来,beacause
原因: 直接写代码操作数据库有以下问题
1.不知道MySql数据库的操作方式,解析方式;
2.代码非常繁琐,非常的麻烦;
3.MySQL和Oracle数据库的操作方式与解析方式不同、每个数据可都要写一套代码;
4,MySQL和Oracle数据库相互切换非常麻烦
现在
2.JDBC规范定义接口,具体的实现有个大数据库厂商来实现
JDBC是Java访问数据库的标准规范。真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用JDBC接口中的方法即可。数据库驱动由数据库厂商提供。
好处:1我们只需要调用JDBC接口中的方法使用简单;
2.使用通一套代码,进行少量修改就可以访问其他数据库;
2.1JDBC所用的包
- java.sql:JDBC访问数据库的基础包,在JavaSE中的包。如:java.sql.Connection
- javax.sql: JDBC访问数据库的扩展包
- 数据库的驱动,各大数据库厂商来实现。如:MySQL的驱动:com.mysql.jdbc.Driver
3.JDBC四个核心对象
- DriverManager: 用于注册驱动
- Connection: 表示与数据库创建的连接
- Statement: 执行SQL语句的对象
- ResultSet: 结果集或一张虚拟表
4.注册驱动使用步骤
Class.forName("com.mysql.jdbc.Driver");
// 连接到MySQL
// url: 连接数据库的URL
// user: 数据库的账号
// password: 数据库的密码
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名", "root", "root");
System.out.println(conn);//连接成功控制台显示 com.mysql.jdbc.JDBC4Connection@389177le(这个是连接对象connection)
}
5.statement对象API介绍
1.boolean execute(String sql)
//此方法可以执行任意sql语句。返回boolean值,表示是否返回ResultSet结果集。仅当执行select语句,且有返回结果时返回true, 其它语句都返回false;
int executeUpdate(String sql)
//根据执行的DML(INSERT、UPDATE、DELETE)语句,返回受影响的行数
ResultSet executeQuery(String sql)
//根据查询语句返回结果集,只能执行SELECT语句
注意:在MySQL中,只要不是查询就是修改。
executeUpdate:用于执行增删改 executeQuery:用于执行查询
6.代码演示
public class StatementDemo {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///num1? characterEncoding=utf8", "root", "root");
System.out.println(conn);
// String sql = "SELECT * FROM category;";
// 从连接中拿到一个Statement对象
Statement stmt = conn.createStatement();
// 1.插入记录
String sql = "INSERT INTO category (cname) VALUES ('手机');";
int i = stmt.executeUpdate(sql);
System.out.println( i);
// 2.修改记录
sql = "UPDATE category SET cname='汽车' WHERE cid=4;";
i = stmt.executeUpdate(sql);
System.out.println(i);
// 3.删除记录
sql = "DELETE FROM category WHERE cid=1;";
i = stmt.executeUpdate(sql);
System.out.println(i);
// 释放资源
stmt.close();
conn.close();
}
}
6.数据库对于表的查询
ResultSet用于保存执行查询SQL语句的结果。
我们不能一次性取出所有的数据,需要一行一行的取出。
ResultSet的原理:
1. ResultSet内部有一个指针,刚开始记录开始位置
2. 调用next方法, ResultSet内部指针会移动到下一行数据
3. 我们可以通过ResultSet得到一行数据 getXxx得到某列数据
ResultSet获取数据的API
其实ResultSet获取数据的API是有规律的get后面加数据类型。我们统称getXXX()
7.使用JDBC查询数据库中的数据的步骤
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///num1", "root", "root");
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM student;";
ResultSet rs = stmt.executeQuery(sql);
// 内部有一个指针,只能取指针指向的那条记录
while (rs.next()) { // 指针移动一行,有数据才返回true
// 取出数据
int cid = rs.getInt("cid");
String cname = rs.getString("cname");
String habby = rs.getString("habby")
System.out.println("id: " +cid + "==" + "name: "+name+"=="+"habby: "+habby);
}
// 关闭资源
rs.close();
stmt.close();
conn.close();
}
总结:其实我们使用JDBC操作数据库的步骤都是固定的。不同的地方是在编写SQL语句
- 注册驱动
- 获取连接
- 获取到Statement
- 使用Statement执行SQL
- ResultSet处理结果
- 关闭资源
8.JDBC操作事物使用步骤
- 注册驱动
2. 获取连接
3. 获取到Statement
4. 开启事务
5. 使用Statement执行SQL
6. 提交或回滚事务
7. 关闭资源
8.1代码实现
public static void main(String[] args) {
Connection conn = null;
try {
// 拿到连接
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///num1", "root", "root");
// 开启事务
conn.setAutoCommit(false);
Statement pstmt = conn.createStatement();
// 老王减500
String sql = "UPDATE account SET balance = balance - 500 WHERE id=1;";
pstmt.executeUpdate(sql);
// 模拟异常
// int i = 10 / 0;
// 老关加500
sql = "UPDATE account SET balance = balance + 500 WHERE id=2;";
pstmt.executeUpdate(sql);
pstmt.close();
// 成功,提交事务
System.out.println("成功,交互成功");
conn.commit();
} catch (Exception e) {
// 失败,回滚事务
try {
System.out.println("出了异常,回滚事物,出错");
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
二、mybaties
1.概述
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation
迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
作为持久层的框架,还有一个封装程度更高的框架就是Hibernate,但这个框架因为各种原因目前在国内的流行程度下降太多,现在公司开发也越来越少使用。
1.mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而且不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
2.mybatis 通过 xml或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为
java 对象并返回。
3.采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
(orm是对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换
。从效果上说,它其实是创建了一个可在编程语言里使用的–“虚拟对象数据库”。)
1.1mybaties对比jdbc优势
1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
2、Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变Java代码。
3、使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
4、对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
2.mybaties实现dao开发方式
mapper实现方式
Mapper 接口开发方法只需要程序员编写 Dao 接口,由 Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同
Dao 接口实现类方法。1、 Mapper.xml 文件中的 namespace 与 Dao接口的类路径相同。
2、 Dao接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同
3、 Dao接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
4、 Dao接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同
org.mybatis
mybatis
3.4.5
junit
junit
4.10
test
mysql
mysql-connector-java
5.1.6
runtime
log4j
log4j
1.2.12
SqlSessionConfig.xml配置文件顺序
properties(属性) 导入 .properties 内容
settings(全局配置参数) 懒加载的时候用
typeAliases(类型别名)
为某个java类起名 ,配置别名:在MyBatis中为一个类取别名 配置别名是为了在对象映射文件中接收参数类型和返回参数类型时使用
typeHandlers(类型处理器)
objectFactory(对象工厂)
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。
如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。plugins(插件)
简单理解为拦截器,既然是拦截器说白了一般都是动态代理来实现对目标方法的拦截,在前后做一些操作。
environments(环境集合属性对象)
首先获取标签元素的default属性,这个属性作用就是指定当前情况下使用哪个数据库配置,也就是使用哪个节点的配置,default的值就是配置的标签元素的id值。
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
用来在mybatis初始化的时候,告诉mybatis需要引入哪些Mapper映射文件。
mapper配置文件
/*获取主键*/
SELECT LAST_INSERT_ID()
INSERT INTO contact(name,sex,age,address,qq,email) VALUES (#{name},#{sex},#{age},# {address},#{qq},#{email})
update contact set name=#{name} , sex=#{sex}, age=#{age}, address=#{address}, qq=#{qq}, email=#{email} where id=#{id}
delete from contact where id = #{id}
Contact实体类
private Integer id;
private String name;
private String sex;
private Integer age;
private String address;
private Integer qq;
private String email;
持久层接口ContactDao
public interface ContactDao {
List selectFindContact();
int saveContact(Contact contact);
int updateContactById(Contact contact);
int deleteContactById(Integer id);
}
测试类test
public static void main(String[] args) {
private InputStream res;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory build;
private SqlSession sqlSession;
private ContactDao mapper;
@Before
public void before() {
try {
//加载配置文件
res = Resources.getResourceAsStream("SqlSessionConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
builder = new SqlSessionFactoryBuilder();
//构建sqlsession工厂
build = builder.build(res);
//sqlSession 相当于connection
// session 会话 指 程序和数据库的连接 底层封装的就是connection的操作
sqlSession = build.openSession();
mapper = sqlSession.getMapper(ContactDao.class);
}
@After
public void test5(){
//mybaties 事物是默认开启的必须要提交
sqlSession.commit();
}
@Test
public void test1() {
List contact = mapper.selectFindContact();
for (Contact contact1 : contact) {
System.out.println(contact1);
}
}
@Test
public void test() {
Contact contact = new Contact("关某", "男", 11, "天安门", 111111, "[email protected]");
int i = mapper.saveContact(contact);
System.out.println(i);
//查询主键ID
Integer id = contact.getId();
System.out.println(id);
System.out.println(i);
}
//修改
@Test
public void test3(){
Contact contact = new Contact();
contact.setId(29);
contact.setAge(14);
int i = mapper.updateContactById(contact);
System.out.println(i);
}
//删除
@Test
public void test4() {
int i = mapper.deleteContactById(2);
System.out.println(i);
}
2.1resultMap
如果数据库列名与实体类的属性不一致时,我们就不能封装结果集(resultType)到指定的实体对象。
<--过改别名的方式,现在返回结果集的列名已经与 User 类的属性名不相同了。-->
2.2Mybatis 代理方式实现 DAO 源码分析
getMapper(Class type, SqlSession sqlSession)
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
}
//当调用方法时,会去调用 MapperProxy 的 invoke()方法;
//这个 invoke()的最后一行:
return mapperMethod.execute(sqlSession, args);
//继续往下找这个方法:execute(sqlSession, args);
下面是它的方法体
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " +
command.getName());
}
2.3映射文件动态方式
1.if
2.foreach
3.mybaties延迟加载
延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
dao
public interface AccountDao {
List findAllSelect();
}
pojo
public class Account {
private Integer id;
private Integer uid;
private String money ;
private User user;
}
public class User {
private String id;
private String username;
private String age;
private String sex;
private String address;
}
test
//多表查询
//懒加载全查
@Test
public void test4(){
AccountDao mapper = sqlSession.getMapper(AccountDao.class);
List allSelect = mapper.findAllSelect();
for (Account account : allSelect) {
System.out.println(account);
}
}
/*结果:2019-04-06 22:08:16,316 537 [ main] DEBUG t.dao.AccountDao.findAllSelect - ==> Preparing: select * from account
2019-04-06 22:08:16,341 562 [ main] DEBUG t.dao.AccountDao.findAllSelect - ==> Parameters:
2019-04-06 22:08:16,390 611 [ main] DEBUG t.dao.AccountDao.findAllSelect - <== Total: 3
2019-04-06 22:08:16,391 612 [ main] DEBUG m.jrx.dao.UserDao.findAllId - ==> Preparing: select *from user where id =?
2019-04-06 22:08:16,391 612 [ main] DEBUG m.jrx.dao.UserDao.findAllId - ==> Parameters: 41(Integer)
2019-04-06 22:08:16,394 615 [ main] DEBUG m.jrx.dao.UserDao.findAllId - <== Total: 1
Account{id=1, uid=41, money='1000', user=User{id='41', username='老王', age='null', sex='男', address='北京王府井'}}
2019-04-06 22:08:16,394 615 [ main] DEBUG m.jrx.dao.UserDao.findAllId - ==> Preparing: select *from user where id =?
2019-04-06 22:08:16,394 615 [ main] DEBUG m.jrx.dao.UserDao.findAllId - ==> Parameters: 45(Integer)
2019-04-06 22:08:16,397 618 [ main] DEBUG m.jrx.dao.UserDao.findAllId - <== Total: 1
Account{id=2, uid=45, money='1000', user=User{id='45', username='小刘', age='null', sex='男', address='北京王府井'}}
Account{id=3, uid=41, money='2000', user=User{id='41', username='老王', age='null', sex='男', address='北京王府井'}}*/
只查一个数据
@Test
public void test5(){
AccountDao mapper = sqlSession.getMapper(AccountDao.class);
List allSelect = mapper.findAllSelect();
for (Account account : allSelect) {
System.out.println(account.getId());
}
}
/*结果
2019-04-06 22:10:44,984 522 [ main] DEBUG source.pooled.PooledDataSource - Created connection 1971851377.
2019-04-06 22:10:44,984 522 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@75881071]
2019-04-06 22:10:44,986 524 [ main] DEBUG t.dao.AccountDao.findAllSelect - ==> Preparing: select * from account
2019-04-06 22:10:45,011 549 [ main] DEBUG t.dao.AccountDao.findAllSelect - ==> Parameters:
2019-04-06 22:10:45,055 593 [ main] DEBUG t.dao.AccountDao.findAllSelect - <== Total: 3
1
2
3
*/
4.注解开发
public interface UserDao {
@Insert("insert into user values(null,#{username},#{birthday},#{sex},#{address})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void saveUser(User user);
@Update("update user set address = #{address} ,sex = #{sex} where username = #{username}")
void upDate(User user);
@Delete("delete from user where id=#{id}")
void deleteUser(User user);
@Select("select *from user")
List selectList();
/*
* @Result(
* property = "user" 封装数据到哪个属性
* ,javaType = User.class 返回值的类型
* column = "uid" 调用方法时 传递的参数
* )
* @One 一张表对一张表
* select ="" 查询 定位到sql即可 全限定类名.方法名称
* fetchType = "" 抓取类型 调用查询用户方法的时机
* FetchType.LAZY 懒加载 全局配置true
* */
@Select("select * from account")
@Results(value={
@Result(id = true , column = "id" , property = "id"),
@Result(column = "money" , property = "money"),
/*配置外键*/
@Result(property = "user" ,javaType = User.class , column = "id",
one = @One(select ="com.itheima.dao.UserDao.findUserById" , fetchType = FetchType.LAZY) )
})
List findAllAccountAndUser();
三、JPA
1.概述
JPA 的全称是 Java Persistence API(java持久化语言),是 SUN 公司推出的一套基于 ORM
的规范(接口),内部是由一系列的接口和抽象类构成;ORM通过编程的方式将对象类型转换成关系类型。主要特点是将object映射成数据库中的数据。
2.实现
junit
junit
4.9
test
org.hibernate
hibernate-entitymanager
${project.hibernate.version}
org.hibernate
hibernate-c3p0
${project.hibernate.version}
log4j
log4j
1.2.17
mysql
mysql-connector-java
5.1.6
配置文件
pojo
@Entity //声明实体类
@Table(name="cst_customer")
//建立类与表的映射关系。如果类名和表名一致,则此注解可以不写
public class Customer {
@Id //建立与表中主键的映射关系
@Column(name = "cust_id") //建立属性名与表中字段的映射关系。标的列名和字段名一样,就不用写
@GeneratedValue(strategy = GenerationType.IDENTITY) //对应AUTO_INCREMENT
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_phone")
private String custPhone;
}
jpa 工具类
private static EntityManagerFactory managerFactory;
static{
//创建实体类管理器工厂(相当于连接池)
managerFactory = Persistence.createEntityManagerFactory("jpaUnit");
}
//获得连接
public static EntityManager getEntityManager(){
return managerFactory.createEntityManager();
}
test
private EntityManager em;
private EntityTransaction tx
@Before
public void before(){
// 获取实体管理对象
em= JPAUtil.getEntityManager();
// 获取事务对象
tx=em.getTransaction();
// 开启事务
tx.begin();
}
@After
public void after(){
tx.commit();
em.close();
}
//增加
@test
public void test(){
Customer c = new Customer();
c.setCustName("中国社会科学院");
c.setCustAddress("王府井甲一号");
em.persist(c);
}
//查询
@Test
public void test2(){
Customer c = em.find(Customer.class, 1L);
System.out.println(c);
}
@Test
public void test3(){
Customer c = em.find(Customer.class, 1L);
c.setCustName("中国社会最厉害科学院");
em.merge(c);//修改合并
}
//删除
@Test
public void test4(){
Customer c = em.find(Customer.class, 1L);
em.remove(c);
}
//条件查询
public void test5 () {
String jpql = "from Customer where custName like ? ";
Query query = em.createQuery(jpql);
//对占位符赋值,从 1 开始
query.setParameter(1, "科学大牛%");
//查询并得到返回结果
Object object = query.getSingleResult(); //得到唯一的结果集对象
System.out.println(object);
}
总结
1.JDBC是较底层的持久层操作方式,它的的工作量比较大,需要连接,然后处理jdbc底层事务,处理数据类型,还需要操作Connection,Statement对象和ResultSet对象去拿数据并关闭他们,所以用的少;
2.mybaties 比jdbc操作简单 ,代码量减少了一半以上,而且SQL写在XML里,从程序代码中彻底分离,降低耦合度,MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
3.jpa最主要的特点就是使用简单。JPA底层使用关系数据库进行存储,因此具备关系数据库的特点,例如事务性、数据完整性、并发访问、大数据量等。