项目学生:Spring数据的持久性

这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client,带有Jersey的 Webservice Server业务层

RESTful webapp onion的最后一层是持久层。

持久层有两种哲学。 一个阵营将数据库视为一个简单的存储,并希望保持这一层非常薄。 另一阵营知道,在数据库中执行任务通常比访问数据库中的数据,用Java完成必要的工作以及第二次访问结果要快得多。 该阵营通常需要一个相当厚的持久层。

第二个阵营也有一群希望广泛使用存储过程的流放者。 这使数据库功能更加强大,但在持久层中却付出了一些额外的复杂性。 它的主要缺点是存储过程是特定于数据库的。

第二批流亡者仅使用存储过程来提高安全性。 我已经在前面进行了讨论,例如,您应该使用用户名和密码来调用存储过程并获得“接受”或“拒绝”响应,而不是检索哈希密码并在应用程序中进行比较。 第一种方法允许您使用数据库的GRANT和REVOKE特权将哈希密码存储在不可访问的表中,即使攻击者可以以应用程序用户身份执行任意SQL注入。

(旁注:您通常可以用Java编写存储过程!Oracle支持它,PostgreSQL支持它(通过PL / Java扩展),H2支持它(通过类路径)。我不知道MySQL是否支持它。这种方法有确定的成本,但这可能是解决许多问题的最佳解决方案。)

无论如何,该项目目前仅支持CRUD操作,它们是使用薄型持久层的经典示例。 但是添加“ thick”方法很容易–只需使用它们创建一个CourseRepositoryImpl类。 (此类不应实现CourseRepository接口!)

设计决策

Spring Data –我们正在使用Spring Data,因为它会自动生成持久层类。

局限性

分页 –没有尝试支持分页。 由于Spring Data已经支持它,所以这不是一个大问题-我们只需要编写胶水即可。

组态

基本配置仅需要@EnableJpaRepositories批注。

使用纯内存嵌入式数据库的集成测试需要更多的工作。 即使使用H2网址(告诉它使数据库服务器不工作),也不能直接使用Spring嵌入式数据库。 数据库已正确初始化,但在测试实际运行之前已关闭。 由于缺少数据库架构,结果是失败。

使用由文件支持的内存数据库是微不足道的,但可能导致并发运行问题,意外拉入旧的测试数据等。显而易见的解决方案是使用随机临时支持文件,但这种方法引入了自己的问题。

下面的方法是将嵌入式数据库缓存在配置类中,并仅在应用程序关闭时销毁它。 这引入了一些非显而易见的行为,迫使我们也必须显式创建一些其他bean。

(IIRC,如果您在配置类中创建嵌入式数据库,并在配置文件中创建事务Bean,则spring在配置文件中创建幻像数据源,并且初始化失败。当我开始在同一文件中创建事务Bean时,此问题消失了放置为数据源。)

@Configuration
@EnableJpaRepositories(basePackages = { "com.invariantproperties.sandbox.student.repository" })
@EnableTransactionManagement
@PropertySource("classpath:test-application.properties")
@ImportResource("classpath:applicationContext-dao.xml")
public class TestPersistenceJpaConfig implements DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(TestPersistenceJpaConfig.class);

    private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
    private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
    private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
    private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
    // private static final String PROPERTY_NAME_PERSISTENCE_UNIT_NAME =
    // "persistence.unit.name";

    @Resource
    private Environment environment;

    private EmbeddedDatabase db = null;

    @Bean
    public DataSource dataSource() {
        final EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        db = builder.setType(EmbeddedDatabaseType.H2).build(); // .script("foo.sql")
        return db;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws ClassNotFoundException {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();

        bean.setDataSource(dataSource());
        bean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
        bean.setPersistenceProviderClass(HibernatePersistence.class);
        // bean.setPersistenceUnitName(environment
        // .getRequiredProperty(PROPERTY_NAME_PERSISTENCE_UNIT_NAME));

        HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
        bean.setJpaVendorAdapter(va);

        Properties jpaProperties = new Properties();
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT,
environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL,
                environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY,
                environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,
                environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO,
                environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));

        bean.setJpaProperties(jpaProperties);

        return bean;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();

        try {
            tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
        } catch (ClassNotFoundException e) {
            // TODO: log.
        }

        return tm;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    @Override
    public void destroy() {
        if (db != null) {
            db.shutdown();
        }
    }
}

applicationContext.xml文件为空。 该属性文件是:

# hibernate configuration
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
hibernate.show_sql=false
hibernate.format_sql=true
hibernate.hbm2ddl.auto=create

# jpa configuration
entitymanager.packages.to.scan=com.invariantproperties.sandbox.student.domain
persistence.unit.dataSource=java:comp/env/jdbc/ssDS
persistence.unit.name=ssPU

单元测试

由于所有代码都是自动生成的,因此没有单元测试。 这将随着添加自定义方法而改变。

整合测试

现在,我们可以编写业务层的集成测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { BusinessApplicationContext.class, TestBusinessApplicationContext.class,
        TestPersistenceJpaConfig.class })
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class CourseServiceIntegrationTest {

    @Resource
    private CourseService dao;

    @Test
    public void testCourseLifecycle() throws Exception {
        final String name = "Calculus 101";

        final Course expected = new Course();
        expected.setName(name);

        assertNull(expected.getId());

        // create course
        Course actual = dao.createCourse(name);
        expected.setId(actual.getId());
        expected.setUuid(actual.getUuid());
        expected.setCreationDate(actual.getCreationDate());

        assertThat(expected, equalTo(actual));
        assertNotNull(actual.getUuid());
        assertNotNull(actual.getCreationDate());

        // get course by id
        actual = dao.findCourseById(expected.getId());
        assertThat(expected, equalTo(actual));

        // get course by uuid
        actual = dao.findCourseByUuid(expected.getUuid());
        assertThat(expected, equalTo(actual));

        // update course
        expected.setName("Calculus 102");
        actual = dao.updateCourse(actual, expected.getName());
        assertThat(expected, equalTo(actual));

        // delete Course
        dao.deleteCourse(expected.getUuid());
        try {
            dao.findCourseByUuid(expected.getUuid());
            fail("exception expected");
        } catch (ObjectNotFoundException e) {
            // expected
        }
    }

    /**
     * @test findCourseById() with unknown course.
     */
    @Test(expected = ObjectNotFoundException.class)
    public void testfindCourseByIdWhenCourseIsNotKnown() {
        final Integer id = 1;
        dao.findCourseById(id);
    }

    /**
     * @test findCourseByUuid() with unknown Course.
     */
    @Test(expected = ObjectNotFoundException.class)
    public void testfindCourseByUuidWhenCourseIsNotKnown() {
        final String uuid = "missing";
        dao.findCourseByUuid(uuid);
    }

    /**
     * Test updateCourse() with unknown course.
     * 
     * @throws ObjectNotFoundException
     */
    @Test(expected = ObjectNotFoundException.class)
    public void testUpdateCourseWhenCourseIsNotFound() {
        final Course course = new Course();
        course.setUuid("missing");
        dao.updateCourse(course, "Calculus 102");
    }

    /**
     * Test deleteCourse() with unknown course.
     * 
     * @throws ObjectNotFoundException
     */
    @Test(expected = ObjectNotFoundException.class)
    public void testDeleteCourseWhenCourseIsNotFound() {
        dao.deleteCourse("missing");
    }
}

源代码

参考: 项目学生: Invariant Properties博客上来自我们JCG合作伙伴 Bear Giles的Spring数据的持久性

翻译自: https://www.javacodegeeks.com/2014/01/project-student-persistence-with-spring-data.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值