1.intro
Very often, on DB level there are a lot of classes(DAOs) with similar methods:To avoid that - there are lot of different strategies. In my blog a described some of them : 1. using genericDao 2. EasyJDBC tool
Spring Data - another approach : you just have to create an interface (which can be extended from default CRUD repository) and leave it without implementation. String will generate implementation for you!
2.project structure
Everything is pretty simple : one entity (Category), one dao-repository(CategoryRepository) interface, one config class for spring and application runner(class with main procedure):3. project pom.xml
Just 3 dependencies here : spring data, persistence api for annotation support, H2 database.<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demien</groupId> <artifactId>sdata</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.3.4.RELEASE</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.2.5.Final</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.3.173</version> </dependency> </dependencies> </project>
4. app.properties
Properties for H2 DB and hibernate#DB properties:db.driver=org.h2.Driverdb.url=jdbc:h2:~/testdb.username=sadb.password=sa #Hibernate Configuration:db.hibernate.dialect=org.hibernate.dialect.H2Dialectdb.hibernate.show_sql=falsedb.entitymanager.packages.to.scan=com.demien.sdata.domaindb.hibernate.hbm2ddl.auto = create
5. test entity
It's a very simple entity with persistence api annotations.package com.demien.sdata.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; @Entity(name = "CATEGORY") public class Category { @Id @Column(name = "CATEGORY_ID") private Long categoryId; @Column(name="CATEGORY_NAME") private String categoryName; @Column(name = "CATEGORY_DESCRIPTION") private String categoryDescription; @Column(name="PARENT_CATEGORY_ID") private Long parentCategoryId; public Category() {} public Long getCategoryId() { return categoryId; } public void setCategoryId(Long categoryId) { this.categoryId = categoryId; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public String getCategoryDescription() { return categoryDescription; } public void setCategoryDescription(String categoryDescription) { this.categoryDescription = categoryDescription; } public Long getParentCategoryId() { return parentCategoryId; } public void setParentCategoryId(Long parentCategoryId) { this.parentCategoryId = parentCategoryId; } @Override public String toString() { return "Category{" + "categoryId=" + categoryId + ", categoryName='" + categoryName + '\'' + '}'; } }
6. repository
As I mention before, we just have to create interface - implementation will be created by Spring. Interface extended from JpaRepository - it has definition of all base db methods (like save, find, delete...). I added only one custom method to interface, and again : it's just a method without implementation.package com.demien.sdata.repository; import com.demien.sdata.domain.Category; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; @Repository(value = "CategoryRepository") public interface CategoryRepository extends JpaRepository<Category, Long> { @Query("FROM CATEGORY where CATEGORY_NAME like %?1% ") List<Category> findByPattern(String pattern); }
7. spring config
XML configs are not very popular at modern time, so I used XML-less Spring configuration.package com.demien.sdata; import org.hibernate.ejb.HibernatePersistence; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Properties; @Configuration@EnableTransactionManagement@ComponentScan("com.demien.sdata") @PropertySource("classpath:app.properties") @EnableJpaRepositories("com.demien.sdata.repository") public class AppConfig { private static final String PROP_DATABASE_DRIVER = "db.driver"; private static final String PROP_DATABASE_PASSWORD = "db.password"; private static final String PROP_DATABASE_URL = "db.url"; private static final String PROP_DATABASE_USERNAME = "db.username"; private static final String PROP_HIBERNATE_DIALECT = "db.hibernate.dialect"; private static final String PROP_HIBERNATE_SHOW_SQL = "db.hibernate.show_sql"; private static final String PROP_ENTITYMANAGER_PACKAGES_TO_SCAN = "db.entitymanager.packages.to.scan"; private static final String PROP_HIBERNATE_HBM2DDL_AUTO = "db.hibernate.hbm2ddl.auto"; @Resourceprivate Environment env; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getRequiredProperty(PROP_DATABASE_DRIVER)); dataSource.setUrl(env.getRequiredProperty(PROP_DATABASE_URL)); dataSource.setUsername(env.getRequiredProperty(PROP_DATABASE_USERNAME)); dataSource.setPassword(env.getRequiredProperty(PROP_DATABASE_PASSWORD)); return dataSource; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setDataSource(dataSource()); entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class); entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN)); entityManagerFactoryBean.setJpaProperties(getHibernateProperties()); return entityManagerFactoryBean; } @Bean public JpaTransactionManager transactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } private Properties getHibernateProperties() { Properties properties = new Properties(); properties.put("hibernate.dialect", env.getRequiredProperty(PROP_HIBERNATE_DIALECT)); properties.put("hibernate.show_sql", env.getRequiredProperty(PROP_HIBERNATE_SHOW_SQL)); properties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty(PROP_HIBERNATE_HBM2DDL_AUTO)); return properties; } }
8. main runner
I'm starting Spring context and testing main repository methods herepackage com.demien.sdata; import com.demien.sdata.domain.Category; import com.demien.sdata.repository.CategoryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.util.Arrays; /** * Created by dmitry on 05.10.15. */public class App { private static ApplicationContext applicationContext; public static void main(String args[]) { applicationContext=new AnnotationConfigApplicationContext(AppConfig.class); new App().test(); } public void test() { CategoryRepository categoryRepository=(CategoryRepository)applicationContext.getBean("CategoryRepository"); Category category=new Category(); category.setCategoryId(1l); category.setCategoryName("Test 1"); categoryRepository.save(category); category=new Category(); category.setCategoryId(2l); category.setCategoryName("Test 2"); categoryRepository.save(category); System.out.println("\n categoryRepository.findAll()"); System.out.println(Arrays.toString(categoryRepository.findAll().toArray())); System.out.println("\n categoryRepository.findOne(1l)"); System.out.println(categoryRepository.findOne(1l)); System.out.println("\n categoryRepository.findByPattern(2)"); System.out.println(Arrays.toString(categoryRepository.findByPattern("2").toArray())); category.setCategoryName("new name"); categoryRepository.save(category); System.out.println("\n UPDATE to new name for element with id=2"); System.out.println(Arrays.toString(categoryRepository.findAll().toArray())); categoryRepository.delete(category); System.out.println("\n DELETE of element with id=2"); System.out.println(Arrays.toString(categoryRepository.findAll().toArray())); } }
9. results
Everything is working as expected:categoryRepository.findAll()
[Category{categoryId=1, categoryName='Test 1'}, Category{categoryId=2, categoryName='Test 2'}]
categoryRepository.findOne(1l)
Category{categoryId=1, categoryName='Test 1'}
categoryRepository.findByPattern(2)
[Category{categoryId=2, categoryName='Test 2'}]
UPDATE to new name for element with id=2
[Category{categoryId=1, categoryName='Test 1'}, Category{categoryId=2, categoryName='new name'}]
DELETE of element with id=2
[Category{categoryId=1, categoryName='Test 1'}]
Process finished with exit code 0
10. the end
Spring data - very good option to be used on DAO level of application.Full source code can be downloaded from here.