29

I was using Google guice in my project and now I tried to convert the framework to SpringBoot totally.

I configured the Bean for persistence.xml like below in

@Autowired
@Bean(name = "transactionManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
    LocalContainerEntityManagerFactoryBean lEMF =  new LocalContainerEntityManagerFactoryBean();
    lEMF.setPersistenceUnitName("leaseManagementPU");
    lEMF.setPersistenceXmlLocation("persistence.xml");
    return lEMF;
}

Now I need to configure(Inject) EntityManager em, to do JPA operations like em.persist(), em.find etc... How do I configure, also someone try to explain this with sample code

Bryan J. Diaz
  • 374
  • 2
  • 9
Aravind E
  • 1,031
  • 3
  • 15
  • 25

2 Answers2

63

With Spring Boot its not necessary to have any config file like persistence.xml. You can configure with annotations Just configure your DB config for JPA in the

application.properties

spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@DB...
spring.datasource.username=username
spring.datasource.password=pass

spring.jpa.database-platform=org.hibernate.dialect....
spring.jpa.show-sql=true

Then you can use CrudRepository provided by Spring where you have standard CRUD transaction methods. There you can also implement your own SQL's like JPQL.

@Transactional
public interface ObjectRepository extends CrudRepository<Object, Long> {
...
}

And if you still need to use the Entity Manager you can create another class.

public class ObjectRepositoryImpl implements ObjectCustomMethods{

    @PersistenceContext
    private EntityManager em;

}

This should be in your pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.5.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.11.Final</version>
    </dependency>
</dependencies>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Patrick
  • 12,336
  • 15
  • 73
  • 115
  • Thanks, Is it possible to use JPARepository instead of CRUD repository. whats the diff – Aravind E Oct 12 '15 at 10:56
  • I think this should not be a problem. [Here](http://docs.spring.io/spring-data/jpa/docs/1.3.0.RELEASE/reference/html/jpa.repositories.html) is a good documentation. – Patrick Oct 12 '15 at 11:32
  • 3
    @Patrick what if there are custom datasources connecting to different database. In that case wouldn't you need to use separate entitymanagers ? How would one pass in hibernate properties in such a case ? – user3869813 Jan 23 '17 at 04:18
  • Issue which I am struggling to pass EntityManager into the Tasklet of SpringBtach – Jeff Cook Aug 11 '18 at 17:34
25

Hmmm you can find lot of examples for configuring spring framework. Anyways here is a sample

@Configuration
@Import({PersistenceConfig.class})
@ComponentScan(basePackageClasses = { 
    ServiceMarker.class,
    RepositoryMarker.class }
)
public class AppConfig {

}

PersistenceConfig

@Configuration
@PropertySource(value = { "classpath:database/jdbc.properties" })
@EnableTransactionManagement
public class PersistenceConfig {

    private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String PROPERTY_NAME_HIBERNATE_MAX_FETCH_DEPTH = "hibernate.max_fetch_depth";
    private static final String PROPERTY_NAME_HIBERNATE_JDBC_FETCH_SIZE = "hibernate.jdbc.fetch_size";
    private static final String PROPERTY_NAME_HIBERNATE_JDBC_BATCH_SIZE = "hibernate.jdbc.batch_size";
    private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String[] ENTITYMANAGER_PACKAGES_TO_SCAN = {"a.b.c.entities", "a.b.c.converters"};

    @Autowired
    private Environment env;

     @Bean(destroyMethod = "close")
     public DataSource dataSource() {
         BasicDataSource dataSource = new BasicDataSource();
         dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
         dataSource.setUrl(env.getProperty("jdbc.url"));
         dataSource.setUsername(env.getProperty("jdbc.username"));
         dataSource.setPassword(env.getProperty("jdbc.password"));
         return dataSource;
     }

     @Bean
     public JpaTransactionManager jpaTransactionManager() {
         JpaTransactionManager transactionManager = new JpaTransactionManager();
         transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
         return transactionManager;
     }

    private HibernateJpaVendorAdapter vendorAdaptor() {
         HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
         vendorAdapter.setShowSql(true);
         return vendorAdapter;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {

         LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
         entityManagerFactoryBean.setJpaVendorAdapter(vendorAdaptor());
         entityManagerFactoryBean.setDataSource(dataSource());
         entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
         entityManagerFactoryBean.setPackagesToScan(ENTITYMANAGER_PACKAGES_TO_SCAN);             
         entityManagerFactoryBean.setJpaProperties(jpaHibernateProperties());

         return entityManagerFactoryBean;
     }

     private Properties jpaHibernateProperties() {

         Properties properties = new Properties();

         properties.put(PROPERTY_NAME_HIBERNATE_MAX_FETCH_DEPTH, env.getProperty(PROPERTY_NAME_HIBERNATE_MAX_FETCH_DEPTH));
         properties.put(PROPERTY_NAME_HIBERNATE_JDBC_FETCH_SIZE, env.getProperty(PROPERTY_NAME_HIBERNATE_JDBC_FETCH_SIZE));
         properties.put(PROPERTY_NAME_HIBERNATE_JDBC_BATCH_SIZE, env.getProperty(PROPERTY_NAME_HIBERNATE_JDBC_BATCH_SIZE));
         properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));

         properties.put(AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "none");
         properties.put(AvailableSettings.USE_CLASS_ENHANCER, "false");      
         return properties;       
     }

}

Main

public static void main(String[] args) { 
    try (GenericApplicationContext springContext = new AnnotationConfigApplicationContext(AppConfig.class)) {
        MyService myService = springContext.getBean(MyServiceImpl.class);
        try {
            myService.handleProcess(fromDate, toDate);
        } catch (Exception e) {
            logger.error("Exception occurs", e);
            myService.handleException(fromDate, toDate, e);
        }
    } catch (Exception e) {
        logger.error("Exception occurs in loading Spring context: ", e);
    }
}

MyService

@Service
public class MyServiceImpl implements MyService {

    @Inject
    private MyDao myDao;

    @Override
    public void handleProcess(String fromDate, String toDate) {
        List<Student> myList = myDao.select(fromDate, toDate);
    }
}

MyDaoImpl

@Repository
@Transactional
public class MyDaoImpl implements MyDao {

    @PersistenceContext
    private EntityManager entityManager;

    public Student select(String fromDate, String toDate){

        TypedQuery<Student> query = entityManager.createNamedQuery("Student.findByKey", Student.class);
        query.setParameter("fromDate", fromDate);
        query.setParameter("toDate", toDate);
        List<Student> list = query.getResultList();
        return CollectionUtils.isEmpty(list) ? null : list;
    }

}

Assuming maven project: Properties file should be in src/main/resources/database folder

jdbc.properties file

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=your db url
jdbc.username=your Username
jdbc.password=Your password

hibernate.max_fetch_depth = 3
hibernate.jdbc.fetch_size = 50
hibernate.jdbc.batch_size = 10
hibernate.show_sql = true

ServiceMarker and RepositoryMarker are just empty interfaces in your service or repository impl package.

Let's say you have package name a.b.c.service.impl. MyServiceImpl is in this package and so is ServiceMarker.

public interface ServiceMarker {

}

Same for repository marker. Let's say you have a.b.c.repository.impl or a.b.c.dao.impl package name. Then MyDaoImpl is in this this package and also Repositorymarker

public interface RepositoryMarker {

}

a.b.c.entities.Student

//dummy class and dummy query
@Entity
@NamedQueries({
@NamedQuery(name="Student.findByKey", query="select s from Student s where s.fromDate=:fromDate" and s.toDate = :toDate)
})
public class Student implements Serializable {

    private LocalDateTime fromDate;
    private LocalDateTime toDate;

    //getters setters

}

a.b.c.converters

@Converter(autoApply = true)
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime dateTime) {

        if (dateTime == null) {
            return null;
        }
        return Timestamp.valueOf(dateTime);
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp timestamp) {

        if (timestamp == null) {
            return null;
        }    
        return timestamp.toLocalDateTime();
    }
}

pom.xml

<properties>
    <java-version>1.8</java-version>
    <org.springframework-version>4.2.1.RELEASE</org.springframework-version>
    <hibernate-entitymanager.version>5.0.2.Final</hibernate-entitymanager.version>
    <commons-dbcp2.version>2.1.1</commons-dbcp2.version>
    <mysql-connector-java.version>5.1.36</mysql-connector-java.version>
     <junit.version>4.12</junit.version> 
</properties>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>

    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>

    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate-entitymanager.version}</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql-connector-java.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>${commons-dbcp2.version}</version>
    </dependency>
</dependencies>

<build>
     <finalName>${project.artifactId}</finalName>
     <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>${java-version}</source>
                <target>${java-version}</target>
                <compilerArgument>-Xlint:all</compilerArgument>
                <showWarnings>true</showWarnings>
                <showDeprecation>true</showDeprecation>
            </configuration>
        </plugin>
     </plugins>
</build>

Hope it helps. Thanks

Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
Basit
  • 8,426
  • 46
  • 116
  • 196
  • My pleasure :) You can also use Spring Data JPA in your project. In this way you don't have to write simple queries on your domain classes. Also it will simplify your application even more . Follow this link https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods – Basit Oct 12 '15 at 10:30
  • That's great - finally some documentation on how to do this! Just one question, which I will tackle now that it's clear how to get this far: how do you code it without any hard-coded dependency on the JDBC driver classes and the connection pool classes? – Adam Nov 04 '16 at 17:18
  • In POM there is a dependency of MySQL`mysql-connector-java`. This could be SQLServer or oracle. But in case of SQLServer or Oracle or any proprietary JDBC driver you need to download jar and using maven import it in your maven local repository. You can google. It's very easy. Also for connection pooling In POM there is `commons-dbcp2`. In `PersistenceConfig` see `dataSource()`. I used defaults. But you can configure it according to your needs. Like I said in my answer lot of tutorials on the net ;) :) – Basit Nov 22 '16 at 15:28
  • 2
    Also From hibernate onwards 5.2.0. `hibernate-entitymanager` is no longer needed. Just include `hibernate-core`. But 5.2.0 have problem with `Spring Data JPA` while process collection.` hibernate-core 5.2.1` and above is ok with Spring Data JPA if you are using Spring Data JPA. – Basit Nov 22 '16 at 15:32
  • @Basit : thanks for the info. Quick question : How does `entityManager ` reference get populated, even though there is no bean associated with this name/type? Is it something achieved by Spring behind the scenes and how? – DevdattaK Mar 10 '18 at 21:00
  • @Devdattak Sorry if I am not got your question. But In PersistenceConfig.java you declared a bean `entityManagerFactoryBean()`. As we defined only one factory bean so spring knows that when you use `private EntityManager entityManager` with `@PersistenceContext` annotation in `MyDaoImpl` spring inject the reference of entityManager it creates using `entityManagerFactoryBean()`. In the AppConfig we told spring to scan the package DaoImpl. Are you asking this ? – Basit Mar 14 '18 at 15:24
  • @Basit : yes... that was my question. So, it appears that spring is taking care of this indeed. Thanks. – DevdattaK Mar 21 '18 at 01:26
  • You don't have idea how PersistenceConfig helped me. Thank you! – Ângelo Polotto Apr 06 '21 at 20:46
  • Sometimes, when you have several PersistenceConfig's, as is my case, you need to set a persistence unit name to distingish between them. In PersistenceConfig, in the LocalContainerEntityManagerFactoryBean, simply just do: `factory.setPersistenceUnitName("thePersistenceUnitName");` And in MyDaoImpl, just change `@PersistenceContext` to `@PersistenceContext(unitName = "thePersistenceUnit")` – Francisco M Jan 28 '22 at 13:42
  • Hibernate Dialect is not set? – ABC Sep 16 '22 at 10:51