ORM:O->Object、R->Relational (关系)、M-> Mapping(映射),即对象-关系映射
是将数据库关系映射成实体类的思想
例如JPA中:
数据库表table对应实体类,列Column对应实体类成员变量 这样就不需要自己先创建表,而是由JPA根据实体类来创建
一个简单的Springboot-JPA的Demo
结构:暂时只用Repository层、entity层及测试
application.yml:
spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/jpa?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false jpa: # 设置数据库类型(可使用org.springframework.orm.jpa.vendor包下的Database枚举类) database: mysql # 设置是否打印sql语句 show-sql: true hibernate: # 每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新 ddl-auto: update # 设置hibernate方言,对应mysql的版本,解决mysql与hibernate版本不对应问题 database-platform: org.hibernate.dialect.MySQL8DialectUser类:
package demo.entity; import lombok.Data; import org.hibernate.annotations.GenericGenerator; //java提供的持久层注解 import javax.persistence.*; import static javax.persistence.GenerationType.IDENTITY; /** * Author : zfk * Date : 14:12 */ @Entity @Table(name = "user_") @Data public class User { @Id //JPA接口,IDENTITY主键自增 @GeneratedValue(generator = "idGenerator",strategy = IDENTITY) private String id; //Column 数据库表列,unique不可重复,nullable不可为空 @Column(name = "username_",unique = true,nullable = false) private String username; @Column(name = "password_") private String password; @Column(name = "email_",length = 64) private String email; }持久层,因为JPA自带了很多find方法,内置了sql语句,所有简单的查询不需要写sql
package demo.dao; import demo.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; /** * Author : zfk * Date : 14:31 */ public interface UserRepository extends JpaRepository<User,String> { User findByUsername(String name); }结果:
详细代码地址 - gitee
springboot简化了配置,如果是spring的话就有些麻烦:
spring.xml配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" default-lazy-init="true"> <!-- 扫描service包下所有使用注解的类型 --> <context:component-scan base-package="com.service"/> <!-- 属性文件位置 --> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--2 工 工厂类对象--> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!--配置数据源--> <property name="dataSource" ref="dataSource"/> <!--实体类的包扫描--> <property name="packagesToScan" value="com.entity"/> <!--设置实现厂商JPA实现的特定属性--> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true"/> <property name="generateDdl" value="true"/> <property name="database" value="MYSQL"/> </bean> </property> </bean> <!-- 设置JPA实现厂商的特定属性 --> <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="databasePlatform" value="${hibernate.dialect}"/> <!--自动检查注解的实体和数据表,如果数据库不存在表,会根据实体自动生成--> <property name="generateDdl" value="true"/> </bean> <!-- Jpa 事务配置 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!-- Spring Data Jpa配置 --> <jpa:repositories base-package="com.repository" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory"/> <!-- 使用annotation定义事务 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> </beans>Springboot可以在yml文件设置,当然也可以自定义配置类 配置的相关属性在JpaProperties中定义
有以下5个属性
database数据库类型:由org.springframework.orm.jpa.vendor包下的Database枚举类提供Spring只提供了这些关系型数据库:
database-platform:设置hibernate方言,根据我们选的数据库对应数据库的版本,解决数据库与hibernate版本不对应问题 在org.hibernate.dialect包下,对应了很多数据库版本如mysql:
show-sql:是否打印sql语句上述例子就是打印了sql语句:
openInView:在事务外也可以访问懒加载的数据
generateDdl:项目启动时自动检查注解的实类和数据库表,当数据库表或者字段不存在时,则出具库自动添加相应字段。如果数据库存在的字段,而实体中没有属性则不会自动删除数据库字段
实际上用HibernateProperties的配置即可,ddl-auto有四种参数
create ----每次运行该程序,没有表格会新建表格,表内有数据会清空 create-drop ----每次程序结束的时候会清空表 update ---- 每次运行程序,没有表格会新建表格,表内有数据不会清空,只会更新 validate ---- 运行程序会校验数据与数据库的字段类型是否相同,不同会报错
JPA注解主要是实体类与数据库映射
@Entity:标记一个类为实体类,添加该注解后,会在数据库中创建一个和类名相同的表@Table:当实体类与其映射的数据表名称不一致时,需要使用该注解标注,该注解与Entity注解并列使用,置于实体类注解之前,可以设置3个参数:name:数据库的表名、catalog:数据库目录、schema:数据库模式@Column:实体类属性映射成数据库表的一列,当属性名与列名不同时,可以通过name属性设置@Transient:使用该注解当前属性不需要映射为数据表的一列@ID:声明主键@GeneratedValue:设置主键的生成策略,通过strategy属性指定,有四种策略:IDENTITY:采用数据库 ID 自增长的方式来自增主键字段,Oracle 不支持。 AUTO:JPA 自动选择合适的策略,默认选项。 SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySQL 不支持。 TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
当需要自定义主键生成策略,如使用uuid时就可以使用@GenericGenerator注解,需要@GenericGenerator和 @GeneratedValue一起使用
@Id @GeneratedValue(generator = "idGenerator") @GenericGenerator(name = "idGenerator", strategy = "uuid") private String id;如果自定义主键生成策略,需要重写IdentifierGenerator 接口
通过实现JpaRepository接口可以大大简化数据访问代码
JPA支持接口规范方法名查询,如果在接口中定义的查询方法符合它的命名规则,就可以不用写实现,不符合规范会报错,需要添加@Query注解
JPA会进行方法名解析,如findByUsernam,先把前缀findBy剔除,然后根据绑定的实体类,解析剩下的部分
绑定了User,就去User中查询username属性,存在即根据该属性进行查询
其他更复杂的方法也是根据上述命名规范解析方法名