对象关系映射(ORM)数据访问

一、Hibernate

从Spring Framework 5.0开始,Spring需要Hibernate ORM 4.3或更高版本来支持JPA,甚至需要Hibernate ORM5.0+来针对本机Hibernate会话API进行编程。请注意,Hibernate团队不再维护5.1之前的任何版本。

引入hibernate相关依赖
<!--hibernate需要的依赖包-->
<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.3.6.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-c3p0</artifactId>
        <version>5.3.6.Final</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
</dependencies>
创建数据源

db.properties

ps.datasource.driverClassName=com.mysql.jdbc.Driver
ps.datasource.jdbcUrl=jdbc:mysql://localhost:3305/spring?useTimezone=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&tcpRcvBuf=1024000&useOldAliasMetadataBehavior=true&useSSL=false&rewriteBatchedStatements=true&useAffectedRows=true
ps.datasource.username=root
ps.datasource.password=123456

DbConfigBean.java

@Component
@PropertySource("classpath:db.properties")
public class DbConfigBean {

    @Value("${ps.datasource.driverClassName}")
    private String driverClassName;
    @Value("${ps.datasource.jdbcUrl}")
    private String jdbcUrl;
    @Value("${ps.datasource.username}")
    private String username;
    @Value("${ps.datasource.password}")
    private String password;

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getJdbcUrl() {
        return jdbcUrl;
    }

    public void setJdbcUrl(String jdbcUrl) {
        this.jdbcUrl = jdbcUrl;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

SpringConfig.java

/**
 * 创建默认的数据源对象
 */
@Bean("defaultDataSource")
public DataSource defaultDataSource(DbConfigBean configBean) {
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClass(configBean.getDriverClassName());
    ds.setJdbcUrl(configBean.getJdbcUrl());
    ds.setUser(configBean.getUsername());
    ds.setPassword(configBean.getPassword());
    return ds;
}
创建SessionFactory
/**
 * @MethodName sessionFactory
 * @Description 管理Hibernate的SessionFactory, 可能有多个,这里指定默认
 */
@Bean("defaultSessionFactory")
public SessionFactory defaultSessionFactory(DataSource defaultDataSource) throws IOException {
    LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
    sessionFactoryBean.setDataSource(defaultDataSource);
    // 扫描@Entity注解的包
    sessionFactoryBean.setPackagesToScan("com.hibernate.modules");
    // 设置hibernate的扩展属性
    Properties hibernateProperties = new Properties();
    // 方言
    hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
    // 是否输出所有SQL语句到控制台
    hibernateProperties.put("hibernate.show_sql", false);
    // 如果开启, Hibernate将收集有助于性能调节的统计数据.
    hibernateProperties.put("hibernate.generate_statistics", false);
    // 在SessionFactory创建时,自动检查数据库结构, 这里设置为update,一般也是设置该值
    hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
    // 如果开启, Hibernate将在SQL中生成有助于调试的注释信息, 默认值为false.
    hibernateProperties.put("hibernate.use_sql_comments", false);
    sessionFactoryBean.setHibernateProperties(hibernateProperties);
    // 必须执行afterPropertiesSet,否则sessionFactoryBean.getObject()为null
    sessionFactoryBean.afterPropertiesSet();
    return sessionFactoryBean.getObject();
}
创建事务管理器
/**
 * @MethodName defaultTransactionManager
 * @Description 默认的事务管理器
 * @param defaultDataSource
 * @Return org.springframework.orm.hibernate5.HibernateTransactionManager
 */
@Bean("defaultTransactionManager")
public PlatformTransactionManager defaultTransactionManager(DataSource defaultDataSource) throws IOException{
    return new HibernateTransactionManager(defaultSessionFactory(defaultDataSource));
}

同时,在SpringConfig.java上添加@EnableTransactionManagement注解,开启事务支持。

代码示例
通用DAO

BaseDao.java

import java.io.Serializable;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;

/**
 * Hibernate通用的持久层接口
 */
public interface BaseDao<T> {
    /**
     * 保存
     * @param entity
     * @return oid
     */
    Serializable save(T entity);

    /**
     * 更新
     * @param entity
     */
    void update(T entity);

    /**
     * 保存或更新
     * @param entity
     */
    void saveOrUpdate(T entity);

    /**
     * 删除
     * @param entity
     */
    void delete(T entity);

    /**
     * 通过对象标识符获取对象
     * @param oid
     * @return 标识符对应的对象,没找到则返回null
     */
    T findById(Serializable oid);

    /**
     * 查找满足条件的总记录数
     * @param detachedCriteria
     * @return
     */
    Integer findRecordNumByPage(DetachedCriteria detachedCriteria);

    /**
     * 向分页对象中设置记录
     * @param detachedCriteria 离线查询对象
     * @param startIndex 开始索引
     * @param pageSize 每页记录数
     * @return
     */
    List<T> findByPage(DetachedCriteria detachedCriteria, Integer startIndex, Integer pageSize);

    /**
     * 通过条件查询
     * @param detachedCriteria
     * @return
     */
    List<T> findByCriteria(DetachedCriteria detachedCriteria);

    /**
     * @MethodName findByHQL
     * @Description HQL查询
     * @param hql
     * @param params
     * @Return java.util.List<T>
     */
    List<T> findByHQL(String hql, Object... params);


}

BaseDaoImpl.java

import com.hibernate.common.dao.BaseDao;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import javax.annotation.Resource;
import javax.persistence.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;

/**
 * Hibernate通用持久层接口实现类
 */
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
    // 存储泛型的实际参数
    private Class<T> clazz;

    public BaseDaoImpl() {
        // 谁实现该类,这就是谁的类字节码
        Class c = this.getClass();
        // 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type
        Type type = c.getGenericSuperclass();
        // 将类型强转为参数化类型
        ParameterizedType pType = (ParameterizedType) type;
        // 获取该类的父类的所有实际类型参数,也就是泛型的实际参数
        // 这里也就是获取BaseDaoImpl的实际类型参数
        Type[] actualTypeArguments = pType.getActualTypeArguments();
        // 将实际类型参数赋值给成员变量
        clazz = (Class<T>) (actualTypeArguments[0]);
    }

    @Resource(name = "defaultSessionFactory")
    public void setMySessionFactory(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }

    @Override
    public Serializable save(T entity) {
        return this.getHibernateTemplate().save(entity);
    }

    @Override
    public void update(T entity) {
        this.getHibernateTemplate().update(entity);
    }

    @Override
    public void saveOrUpdate(T entity) {
        this.getHibernateTemplate().saveOrUpdate(entity);
    }

    @Override
    public void delete(T entity) {
        this.getHibernateTemplate().delete(entity);
    }

    @Override
    public T findById(Serializable oid) {
        return (T) this.getHibernateTemplate().get(this.clazz, oid);
    }

    @Override
    public Integer findRecordNumByPage(DetachedCriteria detachedCriteria) {
        // 设置记录数投影
        detachedCriteria.setProjection(Projections.rowCount());
        List<Long> list = (List<Long>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
        // 将投影置为空
        detachedCriteria.setProjection(null);
        if (list.size() > 0) {
            return list.get(0).intValue();
        }
        return null;
    }

    @Override
    public List<T> findByPage(DetachedCriteria detachedCriteria, Integer startIndex, Integer pageSize) {
        // 指定hibernate在连接查询时,只封装成一个对象
        detachedCriteria.setResultTransformer(DetachedCriteria.ROOT_ENTITY);
        return (List<T>) this.getHibernateTemplate().findByCriteria(detachedCriteria, startIndex, pageSize);
    }

    @Override
    public List<T> findByCriteria(DetachedCriteria detachedCriteria) {
        return (List<T>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
    }

    @Override
    public List<T> findByHQL(String hql, Object... params) {
        Query query = this.getSessionFactory().getCurrentSession().createQuery(hql);
        for (int i = 0; params != null && i < params.length; i++) {
            query.setParameter(i, params);
        }
        return query.getResultList();
    }

}
实体类
/**
 * @Description 实体类注解
 * @Entity 也有name属性,如果设置了,则在编写HQL时候必须使用该名称,否则则使用类名
 */
@Entity
@Table(name = "os_user")
public class OsUserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "user_name", unique = false, nullable = false)
    private String userName;

    @Column(name = "age", nullable = true)
    private Integer age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
持久化DAO

OsUserDao.java

public interface OsUserDao extends BaseDao<OsUserEntity> {
}

OsUserDaoImpl.java

@Repository
public class OsUserDaoImpl extends BaseDaoImpl<OsUserEntity> implements OsUserDao {

}
业务层service

OsUserService.java

public interface OsUserService {

    long saveUserEntity(OsUserEntity userEntity);
}

OsUserServiceImpl.java

@Service
public class OsUserServiceImpl implements OsUserService {

    @Autowired
    private OsUserDao osUserDao;

    @Transactional
    @Override
    public long saveUserEntity(OsUserEntity userEntity) {
        return ((Long)osUserDao.save(userEntity)).longValue();
    }
}
测试
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextHierarchy({
        @ContextConfiguration(classes = SpringConfig.class),
        @ContextConfiguration(classes = SpringMVCConfig.class)
})
public class HibernateTest {

    @Autowired
    private OsUserService osUserService;

    @Test
    public void testHibernate(){
        OsUserEntity userEntity = new OsUserEntity();
        userEntity.setUserName(UUID.randomUUID().toString());
        userEntity.setAge(29);
        long userId = osUserService.saveUserEntity(userEntity);
        System.out.println("生成的user_id = " + userId);

    }
}
hibernate的相关知识
可选属性配置
属性名用途
hibernate.dialect 一个Hibernate Dialect类名允许Hibernate针对特定的关系数据库生成优化的SQL.

取值 full.classname.of.Dialect

hibernate.show_sql 输出所有SQL语句到控制台. 有一个另外的选择是把org.hibernate.SQL这个log category设为debug

eg. true | false

hibernate.format_sql 在log和console中打印出更漂亮的SQL。

取值 true | false

hibernate.default_schema 在生成的SQL中, 将给定的schema/tablespace附加于非全限定名的表名上.

取值 SCHEMA_NAME

hibernate.default_catalog 在生成的SQL中, 将给定的catalog附加于非全限定名的表名上.

取值 CATALOG_NAME

hibernate.session_factory_name SessionFactory创建后,将自动使用这个名字绑定到JNDI中.

取值 jndi/composite/name

hibernate.max_fetch_depth 为单向关联(一对一, 多对一)的外连接抓取(outer join fetch)树设置最大深度. 值为0意味着将关闭默认的外连接抓取.

取值 建议在03之间取值

hibernate.default_batch_fetch_size 为Hibernate关联的批量抓取设置默认数量.

取值 建议的取值为48, 和16

hibernate.default_entity_mode 为由这个SessionFactory打开的所有Session指定默认的实体表现模式.

取值 dynamic-mapdom4jpojo

hibernate.order_updates 强制Hibernate按照被更新数据的主键,为SQL更新排序。这么做将减少在高并发系统中事务的死锁。

取值 true | false

hibernate.generate_statistics 如果开启, Hibernate将收集有助于性能调节的统计数据.

取值 true | false

hibernate.use_identifer_rollback 如果开启, 在对象被删除时生成的标识属性将被重设为默认值.

取值 true | false

hibernate.use_sql_comments 如果开启, Hibernate将在SQL中生成有助于调试的注释信息, 默认值为false.

取值 true | false

hibernate.hbm2ddl.auto

在SessionFactory创建时,自动检查数据库结构,或者将数据库schema的DDL导出到数据库. 使用create-drop时,在显式关闭SessionFactory时,将drop掉数据库schema。

取值 validate | update | create | create-drop

 方言
RDBMS方言
DB2 org.hibernate.dialect.DB2Dialect
PostgreSQL org.hibernate.dialect.PostgreSQLDialect
MySQL org.hibernate.dialect.MySQLDialect
MySQL with InnoDB org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version) org.hibernate.dialect.OracleDialect
Oracle 9i/10g org.hibernate.dialect.Oracle9Dialect

二、Mybatis

引入依赖包
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.1.1.RELEASE</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.0</version>
</dependency>
<!-- mybatis/spring包 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>
<!-- 导入Mysql数据库链接jar包 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>
<!-- C3p0 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>mchange-commons-java</artifactId>
    <version>0.2.19</version>
</dependency>
创建数据源

db.properties

ps.datasource.driverClassName=com.mysql.jdbc.Driver
ps.datasource.jdbcUrl=jdbc:mysql://localhost:3305/spring?useTimezone=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&tcpRcvBuf=1024000&useOldAliasMetadataBehavior=true&useSSL=false&rewriteBatchedStatements=true&useAffectedRows=true
ps.datasource.username=root
ps.datasource.password=123456

DbConfigBean.java

@Component
@PropertySource("classpath:db.properties")
public class DbConfigBean {

    @Value("${ps.datasource.driverClassName}")
    private String driverClassName;
    @Value("${ps.datasource.jdbcUrl}")
    private String jdbcUrl;
    @Value("${ps.datasource.username}")
    private String username;
    @Value("${ps.datasource.password}")
    private String password;

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getJdbcUrl() {
        return jdbcUrl;
    }

    public void setJdbcUrl(String jdbcUrl) {
        this.jdbcUrl = jdbcUrl;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

SpringConfig.java

@Bean("defaultDataSource")
public DataSource defaultDataSource(DbConfigBean configBean) throws Exception {
    ComboPooledDataSource pooledDataSource = new ComboPooledDataSource();
    pooledDataSource.setDriverClass(configBean.getDriverClassName());
    pooledDataSource.setJdbcUrl(configBean.getJdbcUrl());
    pooledDataSource.setUser(configBean.getUsername());
    pooledDataSource.setPassword(configBean.getPassword());
    return pooledDataSource;
}
配置SqlSessionFactoryBean
@Bean("defaultSessionFactoryBean")
public SqlSessionFactoryBean defaultSessionFactoryBean(DataSource defaultDataSource){
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(defaultDataSource);
    // 扫描mybatis的xml映射文件
    sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations());
    return sqlSessionFactoryBean;
}
private Resource[] resolveMapperLocations() {
    ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    List<String> mapperLocations = new ArrayList<>();
    mapperLocations.add("classpath*:com/mybatis/modules/dao/*Mapper*.xml");
    mapperLocations.add("classpath*:mapper/*Mapper*.xml");
    List<Resource> resources = new ArrayList();
    if (mapperLocations != null) {
        for (String mapperLocation : mapperLocations) {
            try {
                Resource[] mappers = resourceResolver.getResources(mapperLocation);
                resources.addAll(Arrays.asList(mappers));
            } catch (IOException e) {
                // ignore
            }
        }
    }
    return resources.toArray(new Resource[resources.size()]);
}
配置MapperScannerConfigurer
@Bean("defaultMapperScannerConfigurer")
public MapperScannerConfigurer defaultMapperScannerConfigurer(){
    MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
    // DAO接口所在包名,Spring会自动查找其下的类 ,包下的类需要使用@MapperScan注解,否则容器注入会失败
    scannerConfigurer.setBasePackage("com.mybatis.modules.dao");
    scannerConfigurer.setSqlSessionFactoryBeanName("defaultSessionFactoryBean");
    return scannerConfigurer;
}
配置事务管理器
/**
 * @MethodName defaultTransactionManager
 * @Description 默认的事务管理器
 * @param defaultDataSource
 * @Return org.springframework.orm.hibernate5.HibernateTransactionManager
 */
@Bean("defaultTransactionManager")
public PlatformTransactionManager defaultTransactionManager(DataSource defaultDataSource) throws IOException{
    return new DataSourceTransactionManager(defaultDataSource);
}
创建实体类
import java.io.Serializable;

public class OsUserEntity implements Serializable {

    private static final long serialVersionUID = 7362722356438880253L;
    private Long id;
    private String userName;
    private Integer age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;

    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "OsUserEntity{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}
创建持久化接口
/**
 * 这里的@MapperScan就是上面所讲的Mapper扫描器中所需要的配置,会自动生成代理对象。
 * 注意,接口中的方法名称要和对应的MyBatis映射文件中的语句的id值一样,因为生成的
 * 动态代理,会根据这个匹配相应的Sql语句执行。另外就是方法的参数和返回值也需要注
 * 意。接口中的方法如何定义,对应的MyBatis映射文件就应该进行相应的定义。
 */
@MapperScan
public interface OsUserMapper {
    List<OsUserEntity> findAllUser();
    long saveUser(OsUserEntity userEntity);
}

在resource/mapper目录下创建OsUserMapper.xml,内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 为这个mapper指定一个唯一的namespace,namespace="com.mybatis.modules.dao.OsUserMapper"就是com.mybatis.modules.dao(包名)+OsUserMapper-->
<mapper namespace="com.mybatis.modules.dao.OsUserMapper">
    <select id="findAllUser" resultType="com.mybatis.modules.entity.OsUserEntity">
        select id, user_name as userName, age from os_user
    </select>

    <insert id="saveUser" parameterType="com.mybatis.modules.entity.OsUserEntity" useGeneratedKeys="true">
        <selectKey keyProperty="id" order="AFTER" resultType="long">
            select LAST_INSERT_ID()
        </selectKey>
        insert into os_user(user_name, age) values (#{userName}, #{age})
    </insert>
</mapper>
创建服务类

OsUserService.java

public interface OsUserService {
    List<OsUserEntity> findAllUser();
    long saveUserEntity(OsUserEntity userEntity);
}

OsUserServiceImpl.java

@Service
public class OsUserServiceImpl implements OsUserService {

    @Autowired
    private OsUserMapper osUserMapper;

    @Override
    public List<OsUserEntity> findAllUser() {
        return osUserMapper.findAllUser();
    }

    @Transactional
    @Override
    public long saveUserEntity(OsUserEntity userEntity) {
       osUserMapper.saveUser(userEntity);
       return userEntity.getId();
    }
}
测试类
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextHierarchy({
        @ContextConfiguration(classes = SpringConfig.class),
        @ContextConfiguration(classes = SpringMVCConfig.class)
})
public class MybatisTest {
    Logger logger = LoggerFactory.getLogger(MybatisTest.class);

    @Autowired
    private OsUserService osUserService;

    @Test
    public void testQuery(){
        List<OsUserEntity> allUser = osUserService.findAllUser();
        System.out.println("all user" + allUser.toString());
    }
    @Test
    public void testSave(){
        OsUserEntity entity = new OsUserEntity();
        entity.setAge(22);
        entity.setUserName(UUID.randomUUID().toString());
        long id = osUserService.saveUserEntity(entity);
        logger.debug("------------------");
        System.out.println("user id = " + id);
    }
}

扩展:还可以有以下两种方式实现:

1、采用接口org.apache.ibatis.session.SqlSession的实现类org.mybatis.spring.SqlSessionTemplate。mybatis中, sessionFactory可由SqlSessionFactoryBuilder.来创建。MyBatis-Spring 中,使用了SqlSessionFactoryBean来替代。

2、采用抽象类org.mybatis.spring.support.SqlSessionDaoSupport提供SqlSession。

三、JPA

JPA(Java Persistence API)本身是一种规范,它的本质是一种ORM规范(不是ORM框架,因为JPA并未提供ORM实现,只是制定了规范)因为JPA是一种规范,所以,只是提供了一些相关的接口,但是接口并不能直接使用,JPA底层需要某种JPA实现。

Hibernate 从3.2开始,就开始兼容JPA。JPA和Hibernate之间的关系,可以简单的理解为JPA是标准接口,Hibernate是实现,并不是对标关系。Hibernate属于遵循JPA规范的一种实现,但是JPA是Hibernate遵循的规范之一,Hibernate还有其他实现的规范。

Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。

这里我们就使用Spring Data JPA进行说明。

引入依赖
<!-- hibernate -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.3.6.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.3.6.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.3.6.Final</version>
</dependency>
<!-- spring data jpa, 内部依赖了spring其他的模块,版本也不一样,spring版本之间的差距还是比较大的。
    所以这要选择版本时要特别注意,应与当前整体项目使用的版本相同或相近,
    否则可能会报java.lang.NoSuchFieldError: IMPORT_BEAN_NAME_GENERATOR等一些奇奇怪怪的错误。
    例如:我当前项目是spring 5.1.16.RELEASE, 我就选择了2.1.18.RELEASE -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>2.1.18.RELEASE</version>
</dependency>
<!-- C3p0 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>mchange-commons-java</artifactId>
    <version>0.2.19</version>
</dependency>
<!-- mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>
数据源基础配置

db.properties

ps.datasource.driverClassName=com.mysql.jdbc.Driver
ps.datasource.jdbcUrl=jdbc:mysql://localhost:3305/spring?useTimezone=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&tcpRcvBuf=1024000&useOldAliasMetadataBehavior=true&useSSL=false&rewriteBatchedStatements=true&useAffectedRows=true
ps.datasource.username=root
ps.datasource.password=123456

DbConfigBean.java

@Component
@PropertySource("classpath:db.properties")
public class DbConfigBean {

    @Value("${ps.datasource.driverClassName}")
    private String driverClassName;
    @Value("${ps.datasource.jdbcUrl}")
    private String jdbcUrl;
    @Value("${ps.datasource.username}")
    private String username;
    @Value("${ps.datasource.password}")
    private String password;

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getJdbcUrl() {
        return jdbcUrl;
    }

    public void setJdbcUrl(String jdbcUrl) {
        this.jdbcUrl = jdbcUrl;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
jpa相关的bean配置
// 如果需要多个entityManagerFactory,就定义多个拥有@Configuration的多个配置类,使用@EnableJpaRepositories指定相关属性即可
@Configuration
@EnableJpaRepositories(basePackages = {"com.jpa.modules.dao"}, // 扫描dao
        transactionManagerRef = "defaultJpaTransactionManager",
        entityManagerFactoryRef = "defaultEntityManagerFactory")
public class JpaBaseConfig {

    /**
     * @MethodName defaultDataSource
     * @Description 配置数据库连接池
     */
    @Bean("defaultDataSource")
    public DataSource defaultDataSource(DbConfigBean configBean) throws Exception {
        ComboPooledDataSource pooledDataSource = new ComboPooledDataSource();
        pooledDataSource.setDriverClass(configBean.getDriverClassName());
        pooledDataSource.setJdbcUrl(configBean.getJdbcUrl());
        pooledDataSource.setUser(configBean.getUsername());
        pooledDataSource.setPassword(configBean.getPassword());
        return pooledDataSource;
    }

    /**
     * @MethodName defaultEntityManagerFactory
     * @Description 配置entityManagerFactory
     */
    @Bean("defaultEntityManagerFactory")
    public EntityManagerFactory defaultEntityManagerFactory(DataSource defaultDataSource) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(defaultDataSource);
        // 设置实体扫描包
        entityManagerFactoryBean.setPackagesToScan("com.jpa.modules.entity");
        entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);

        //JPA的供应商适配器
        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        jpaVendorAdapter.setGenerateDdl(true);
        jpaVendorAdapter.setDatabase(Database.MYSQL);
        jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
        jpaVendorAdapter.setShowSql(true);
        entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        entityManagerFactoryBean.setJpaDialect(new HibernateJpaDialect());
        entityManagerFactoryBean.afterPropertiesSet();
        return entityManagerFactoryBean.getObject();
    }

    /**
     * @MethodName defaultJpaTransactionManager
     * @Description Jpa 事务管理器
     */
    @Bean("defaultJpaTransactionManager")
    public JpaTransactionManager defaultJpaTransactionManager(EntityManagerFactory defaultEntityManagerFactory) {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(defaultEntityManagerFactory);
        return jpaTransactionManager;
    }

}

注意:在SpringConfig.java中添加@Import(value = {JpaBaseConfig.class}),并开启注解支持@EnableTransactionManagement。

实体类
/**
 * @Description 实体类注解
 * @Entity 也有name属性,如果设置了,则在编写JPQL时候必须使用该名称,否则则使用类名
 */
@Entity
@Table(name = "os_user")
public class OsUserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "user_name", unique = false, nullable = false)
    private String userName;

    @Column(name = "age", nullable = true)
    private Integer age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
持久化接口
import com.jpa.modules.entity.OsUserEntity;
import org.springframework.data.repository.PagingAndSortingRepository;

/**
 * @Description 持久化接口
 * 通过前面的配置可以看出 Spring 对 JPA 的支持已经非常强大,开发者无需过多关注 EntityManager 的创建、事务处理等 JPA 相关的处理
 * ***********************************************************************
 * 在没用使用jpa支持的时候,我们的代码应该是这样的:
 *  1、UserDao 持久层接口
 *  2、UserDaoImpl 持久层实现类
 *  3、UserService 业务层接口 ...... 等等
 * 每写一个实体类,都要衍生出5、6个类来对他进行操作,即使有了注解,我们可以依赖注入方式来拿到实现类。
 * 但是通用的CRUD等操作却不免在每个实现类里声明,你又说,我可以定义一个父类,利用泛型反射原理就可以了,
 * 但那样你还需要为每个Dao声明自己的实现类来继承你的父类 。
 * ***********************************************************************
 * Spring Data Jpa 为我们提供了一些通用的持久化接口,我们只需要声明接口,并继承这些已经封装好的接口即可。
 *  1、Repository:是 Spring Data的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法。
 *  2、CrudRepository:继承Repository,提供增删改查方法,可以直接调用。
 *  3、PagingAndSortingRepository:继承CrudRepository,具有分页查询和排序功能(本类实例)
 *  4、JpaRepository:继承PagingAndSortingRepository,针对JPA技术提供的接口
 *  5、JpaSpecificationExecutor:可以执行原生SQL查询
 *  继承不同的接口,有两个不同的泛型参数,他们是该持久层操作的类对象和主键类型。
 */
public interface UserDao extends PagingAndSortingRepository<OsUserEntity, Long> {
}
测试
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextHierarchy({
        @ContextConfiguration(classes = SpringConfig.class),
        @ContextConfiguration(classes = SpringMVCConfig.class)
})
public class SpringDataJpaTest {

    @Autowired
    private UserDao userDao;

    @Test
    public void testJpa(){
        OsUserEntity entity = new OsUserEntity();
        entity.setUserName(UUID.randomUUID().toString());
        entity.setAge(27);
        userDao.save(entity);
        System.out.println("user_id = " + entity.getId());

        Iterable<OsUserEntity> all = userDao.findAll();
        Iterator<OsUserEntity> iterator = all.iterator();
        while (iterator.hasNext()){
            OsUserEntity userEntity = iterator.next();
            System.out.println(userEntity.getUserName());
        }
    }
}

 

posted @ 2020-07-14 22:07  codedot  阅读(386)  评论(0编辑  收藏  举报