ORM思想和JPA规范,hibernate实现JPA的测试,jpql语句的使用
jdbc的原始操作API
问题收集:
1、是通过实体类和数据库打交道的,那么实体类的属性的类型和数据库的类型怎么对应?
2、为什么不用Springdata JDBC?他和mybatis和Springdata JPA的区别?
3、有多少张表?就要建多少个类吗?
4、原JDBC操作数据库时的两个缺点是:1、操作繁琐。2、占位符赋值麻烦。
5、为什么要使用ORM?
答:因为使用类与表的映射,可以不关注SQL语句的情况下,进行对对象的操作就可以实现对数据库的操作。
6、sun公司为什么要提出ORM思想?
7、JPA(java persistence API)的实现框架hibernate 其中也使用了JDBC吗?
8、JPA是操作关系型数据库,JPA能不能操作非关系型数据库?
JDBC是操作关系型数据库的,能不能操作非关系型数据库?
9、只要是ORM框架,都遵循了JPA规范了吗?
10、mybatis和hibernate都支持哪些数据库?
静态代码块,动态代理和静态代理 ----->AOP
hibernate是一款可以操作大部分关系型数据库的dao层框架,而ORM也是基于关系型数据库提出来的。
一、ORM(Object Relational Mapping,简称ORM)思想是: 主要目的是通过类来操作数据库表,不再重点关注SQL语句;
要建立两个关系, 一:类和表的关系 二:类的属性名和表的字段名的关系
二、JPA(java persistence API)是基于ORM的一套规范,由抽象类和接口组成
Hibernate 、TopLink 都是对JPA的实现。mybatis没有实现JPA。但是他们都是ORM框架。
1)、mybatis(对JDBC进行了封装,是半自动ORM框架,没有实现JPA规范)。
2)、Hibernate (对JDBC进行了轻量级的封装,是全自动ORM框架,自动生成SQL语句。如果要定制SQL的话,需要学习HQL)
jpa操作的操作步骤
1.加载配置文件创建实体管理器工厂
Persisitence:静态方法(根据持久化单元名称创建实体管理器工厂)
createEntityMnagerFactory(持久化单元名称)
作用:创建实体管理器工厂
2.根据实体管理器工厂,创建实体管理器
EntityManagerFactory :获取EntityManager对象
方法:createEntityManager
* 内部维护的很多的内容
内部维护了数据库信息,
维护了缓存信息
维护了所有的实体管理器对象
再创建EntityManagerFactory的过程中会根据配置创建数据库表
* EntityManagerFactory的创建过程比较浪费资源
特点:线程安全的对象
多个线程访问同一个EntityManagerFactory不会有线程安全问题
* 如何解决EntityManagerFactory的创建过程浪费资源(耗时)的问题?
思路:创建一个公共的EntityManagerFactory的对象
* 静态代码块的形式创建EntityManagerFactory
3.创建事务对象,开启事务
EntityManager对象:实体类管理器
beginTransaction : 创建事务对象
presist : 保存
merge : 更新
remove : 删除
find/getRefrence : 根据id查询
Transaction 对象 : 事务
begin:开启事务
commit:提交事务
rollback:回滚
4.增删改查操作
5.提交事务
6.释放资源
import javax.persistence.*;
import java.io.Serializable;
/**
* 所有的注解都是使用JPA的规范提供的注解,
* 所以在导入注解包的时候,一定要导入javax.persistence下的
*
*/
@Entity //声明实体类
@Table(name="cst_customer") //建立实体类和表的映射关系
public class Customer implements Serializable {
/**
* @GeneratedValue:配置主键的生成策略
* strategy
* GenerationType.IDENTITY:自增,mysql
* *底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
* GenerationType.SEQUENCE: 序列,Oracle
* *底层数据库必须支持序列
* GenerationType.TABLE:jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* GenerationType.AUTO: 由程序自动的帮助我们选择主键生成策略
*/
@Id//声明当前私有属性为主键
@GeneratedValue(strategy= GenerationType.IDENTITY) //配置主键的生成策略
@Column(name="cust_id") //指定和表中cust_id字段的映射关系
private Long custId;
@Column(name="cust_name") //指定和表中cust_name字段的映射关系
private String dfdf;
@Column(name="cust_source")//指定和表中cust_source字段的映射关系
private String custSource;
@Column(name="cust_industry")//指定和表中cust_industry字段的映射关系
private String custIndustry;
@Column(name="cust_level")//指定和表中cust_level字段的映射关系
private String custLevel;
@Column(name="cust_address")//指定和表中cust_address字段的映射关系
private String custAddress;
@Column(name="cust_phone")//指定和表中cust_phone字段的映射关系
private String custPhone;
getter和setter方法省略....
}
/**
*JPA的实现hibernate的保存测试
/*
public class SaveTest01 {
@Test
public void test01() {
//创建实体类工厂,借助persistence的静态方法获取
//其中的参数传的是持久化单元的名称,需要jpa文件中指定的
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//创建实体管理类
EntityManager em = factory.createEntityManager();
//获取事务对象
EntityTransaction tx = em.getTransaction();
//开启事务
tx.begin();
//创建实体类对象
Customer c = new Customer();
c.setDfdf("zzzzz");
//执行保存
em.persist(c);
//提交事务
tx.commit();
//释放资源
em.close();
factory.close();
}
}
resource–>META-INF–>persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--配置持久化单元
name:持久化单元名称
transaction-type:事务类型
RESOURCE_LOCAL:本地事务管理
JTA:分布式事务管理 -->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--配置JPA规范的服务提供商 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- 数据库驱动 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<!-- 数据库地址 -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test" />
<!-- 数据库用户名 -->
<property name="javax.persistence.jdbc.user" value="root" />
<!-- 数据库密码 -->
<property name="javax.persistence.jdbc.password" value="root" />
<!--jpa提供者的可选配置:我们的JPA规范的提供者为hibernate,所以jpa的核心配置中兼容hibernate的配 -->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>
pom.xml中的配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
JPA的复杂查询(单表的复杂查询)
JPQL全称Java Persistence Query Language基于首次在EJB2.0中引入的EJB查询语言(EJB
QL),Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
2.jpal语句
学JPQL记住两个点
2.1.JPQL和SQL很像,查询关键字都是一样的
2.唯一的区别是:JPQL是面向对象的
2.2. JPQL书写规则
JPA的查询语言,类似于sql
1.里面不能出现表名,列名,只能出现java的类名,属性名,区分大小写
2.出现的sql关键字是一样的意思,不区分大小写
3.不能写select * 要写select 别名
/**
* 用find查询,根据主键查询
*/
@Test
public void test02() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = new Customer();
//执行该方法的时候就发送SQL查询语句
Customer customer = em.find(Customer.class, 1l);
// System.out.println(customer);
tx.commit();
em.close();
// 不能关闭工厂实体管理类对象,因为下次访问的时候,不能获取到该对象,由于这个对象是由static修饰只运行一次,第二次
// JPAUtil.close();
}
/**
* 用reference查询,根据主键查询
*/
@Test
public void test03() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = new Customer();
//该方法的SQL运行时机是在使用的时候发送查询,此处用了代理对象,如果
//查不到内容的话,会报错。但是find方法就不会,会返回一个null
//这也叫延迟加载或者懒加载,浪费系统性能,只有用到该对象时,才会发送SQL语句来查询
Customer customer = em.getReference(Customer.class, 1l);
// System.out.println(customer);
tx.commit();
em.close();
}
/**
* 删除
*/
@Test
public void test04() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = new Customer();
//先查询出来
Customer customer = em.find(Customer.class, 1l);
//再删除
em.remove(customer);
tx.commit();
em.close();
}
/**
* 修改
*/
@Test
public void test05() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//先查询出来
Customer customer = em.find(Customer.class, 2l);
//再修改
customer.setDfdf("bbbb");
em.merge(customer);
tx.commit();
em.close();
}
复杂查询(单表的复杂查询)
/**
* 查询全部
*/
@Test
public void test06() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String ql = "from Customer";
Query query = em.createQuery(ql);
List<Customer> resultList = query.getResultList();
for (Customer customer : resultList) {
System.out.println(customer);
}
tx.commit();
em.close();
}
/**
* 分页查询
*/
@Test
public void test07() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String ql = "from Customer";
Query query = em.createQuery(ql);
//这里不用limit的原因是持久层框架为了通用性
//设置起始索引 0不包括0
query.setFirstResult(0);
//设置每页显示的条数
query.setMaxResults(2);
List<Customer> resultList = query.getResultList();
for (Customer customer : resultList) {
System.out.println(customer);
}
tx.commit();
em.close();
}
/**
* 条件查询
*/
@Test
public void test08() {
//获得实体管理类
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String ql = "from Customer where custLevel like '2%' ";
Query query = em.createQuery(ql);
// query.setParameter(1, "2%");
Object sr = query.getSingleResult();
System.out.println(sr);
tx.commit();
em.close();
}
这是原始JDBC的操作关系型数据库的代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
/**
* 这是原始JDBC的操作关系型数据库的代码
* JDBC(Java Database Connectivity)是java提供的一套操作关系型 数据库的规范
* 有以下问题:
* 1、操作繁琐 ----------->用jdbc工具类
* 2、占位符赋值麻烦------->
*
*
* 内容在计算机中是以二进制的形式传输。
* 举个例子:一个例子是:当连接数据库时,URL的后边加的characterEncoding=utf8作用是
* 指定发送端和接收端的编码或解码格式,而传送途中是以二进制的形式传送;另外一个例子是:加载properties
* 的文件时,
*/
public class InsertDemo {
public static void main(String[] args) throws Exception {
String sql = "INSERT INTO USER (id,username,PASSWORD,NAME) VALUES(?,?,?,?);";
// JDK8可以不加载驱动
// String driver="com.mysql.jdbc.Driver";
// Class.forName(driver);
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8","root","root");
User user = new User();
user.setUsername("wangwu");
user.setPassword("123");
user.setName("小小小小");
user.setId(10);
PreparedStatement pst = connection.prepareStatement(sql);
pst.setInt(1,user.getId());
pst.setString(2,user.getUsername());
pst.setString(3,user.getPassword());
pst.setString(4,user.getName());
boolean execute = pst.execute();
System.out.println(execute);
}
}
数据库工具类的连接的代码如下,并使用了数据库连接池(druid)
数据库连接池的源码分析 https://www.cnblogs.com/wang-meng/p/5463020.html
其中的配置最小的连接数是连接池一直保持的连接数,最大的连接数是连接池超过最小的连接数后,会创建新的连接,但是用完不会关闭,会归还给连接池。可以创建最大限度的连接就是最大连接数,当超过最大连接数,他们会形成队列,排队等候。
数据库连接池有 Druid ,c3p0 等 静态代码块的详解,点击以下链接
https://blog.csdn.net/qq_35868412/article/details/89360250prepareStatment利用占位符防止SQL注入的原理是:
不是拼接字符串,而是在字符串(“”)中加了一个单引号(‘’),这样就不会改变SQL语句的结构了。
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/*
1. 声明静态数据源成员变量
2. 创建连接池对象
3. 定义公有的得到数据源的方法
4. 定义得到连接对象的方法
5. 定义关闭资源的方法
*/
public class JDBCUtils {
// 1. 声明静态数据源成员变量
private static DataSource ds;
// 2. 创建连接池对象
static {
// 加载配置文件中的数据
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties pp = new Properties();
try {
pp.load(is);
// 创建连接池,使用配置文件中的参数
ds = DruidDataSourceFactory.createDataSource(pp);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 3. 定义公有的得到数据源的方法
public static DataSource getDataSource() {
return ds;
}
// 4. 定义得到连接对象的方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 5.定义关闭资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
// 6.重载关闭方法
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}