📜 ⬆️ ⬇️

In the wake of Spring Pet Clinic. Maven / Spring Context / Spring Test / Spring ORM / Spring Data JPA


Hello!
Spring MVC , according to RevbelLabs Java tools and technologies review for 2014 , is the most popular web framework.
Further the same review names the leader of ORM - Hibernate and the leader of web containers - Apache Tomcat. Add here the most used java script library jQuery , the most popular css framework Bootstrap , still the most popular (unceasingly on the offensive Gradle) build tool Maven , the absolute leader among the test frameworks JUnit and its developers:
Spring Pet Clinic ( demo application ).
In addition to the above, this fairly simple in functionality project also includes Spring-Jdbc, Spring-ORM,
Spring Data JPA ,
Hibernate Validator ,
SLF4J ,
Json Jackson
Jsp
Jstl
WebJars ,
Dandelion DataTables ,
HSQLDB ,
Hamcrest ,
Mockito and dozens of other dependencies.

Progress in software development involves reducing the amount of your own application code, ideally, only to the business logic of the application.
However, this is not given for free - the number of dependencies even exceeded fifty for a simple project (there is 61 jar in WEB-INF \ lib in PetClinic).
Of course, it is not necessary to know them all, some jar catch up in the background, and we don’t even suspect them until we look at the finished war or
run mvn project-info-reports:dependencies (in IDEA: Show Dependencies ... on the Maven project). But you have to work with the main ones. And to fight
some of their features sometimes spend hours or even days. And you have to deal with the bugs of the frameworks themselves ...

Recently, inspired by the Pet Clinic, when I created a webinar on these technologies, I created the Todo Management List application: managing my to-do list
with authorization and user registration. Spring Security has added to Pet Clinic's addictions / very fresh Spring Security
Test and
jQuery jeditable and jQuery notification plugins.
The length of the article does not allow to describe the steps of creating an application (the webinar on creating an application takes 30 hours),
so here I share resources, some thoughts and decisions that came in the process of its creation.
On PaaS Heroku you can find demo applications (first
Once started, a long boot and server error is possible, repeat).

Application examples


On the Internet, many applications built on Spring / JPA / MVC / Security. You can download the sources and choose the solution that suits you best.

Spring namespace configuration


In Spring configuration, there is a tendency to hide implementation details under their namespaces.
The configuration becomes smaller and clearer, but the process of customization or debag is not entirely trivial: first you need to find the beans,
where it is implemented.
Compare for example initialization
bases :
  <bean class="org.springframework.jdbc.datasource.init.DataSourceInitializer" depends-on="entityManagerFactory"> <property name="databasePopulator" ref="resourceDatabasePopulator"/> <property name="dataSource" ref="dataSource"/> </bean> <bean id="resourceDatabasePopulator" class="org.springframework.jdbc.datasource.init.ResourceDatabasePopulator"> <property name="scripts"> <array> <value>classpath*:db/${jdbc.initLocation}</value> <value>classpath*:db/populateDB.sql</value> </array> </property> </bean> 

and
 <jdbc:initialize-database data-source="dataSource" enabled="${database.init}"> <jdbc:script location="classpath:db/${jdbc.initLocation}"/> <jdbc:script location="classpath:db/populateDB.sql"/> </jdbc:initialize-database> 

This is especially evident when comparing the former Acegi Security and Spring Security (all filters are hidden under the namespace security ).
')

@Transactional in tests


It is customary to use transactionalities in Spring tests: after each test is executed, the base rollbacks to the initial state occurs.
However, @Transactional itself greatly influences the behavior of tests: for example, you forgot in the @Transactional service / repository, the test passed, and the application crashed.
Even worse, when in the test they get for comparison the essence from the base:
they fall into the same transactional context and the behavior of the methods being tested becomes somewhat different (it only saves evict or detach).
The state of the base during the test debug is also not displayed until the test transaction has ended.
More honestly use the database initializer before each test:
 <bean class="DbPopulator"> <constructor-arg name="scriptLocation" value="classpath:db/populateDB.sql"/> </bean> 


 public class DbPopulator extends ResourceDatabasePopulator { private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); @Autowired private DataSource dataSource; public DbPopulator(String scriptLocation) { super(RESOURCE_LOADER.getResource(scriptLocation)); } public void execute() { DatabasePopulatorUtils.execute(this, dataSource); } } @ContextConfiguration("classpath:spring/spring-app.xml") @RunWith(SpringJUnit4ClassRunner.class) @ActiveProfiles({"postgres", "jpa"}) public class TodoItemServiceTest { @Autowired private DbPopulator dbPopulator; @Before public void setUp() throws Exception { dbPopulator.execute(); } 


Update: in Spring 4.1, there appeared an annotation replacing the DbPopulator:
 @org.springframework.test.context.jdbc.Sql 

Configure EntityManagerFactory


Having got used to Spring 3.0 to bugs about an entity that was not announced in persistence.xml, I was surprised that everything works without it!
After some digging in the code, I saw that the entire target / classes was scanned for the entity anotation. I was also pleased with the ability to configure JPA without persistence.xml.
You can set specific packages for scanning models, configure provider-specific and general JPA parameters.
Moreover, they can be transferred to a common db.properties file:
  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:dataSource-ref="dataSource" p:packagesToScan="**.model"> <!--p:persistenceUnitName="persistenceUnit">--> <property name="jpaPropertyMap"> <map> <entry> <key> <util:constant static-field="org.hibernate.cfg.AvailableSettings.FORMAT_SQL"/> </key> <value>${hibernate.format_sql}</value> </entry> <entry> <key> <util:constant static-field="org.hibernate.cfg.AvailableSettings.USE_SQL_COMMENTS"/> </key> <value>${hibernate.use_sql_comments}</value> </entry> </map> </property> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" p:showSql="${jpa.showSql}" p:generateDdl="${jpa.generateDdl}"> </bean> </property> </bean> 

Choice of implementation of a pool of connections.


The traditional choice for implementing the DataSource Commons DBCP seems to be losing ground.
According to StackOverflow, for implementation you need to take BoneCP , used in playframework (if you are already using it or are going to,
Note that some effort is required to avoid memory leaks voiced in the report from the developer Plumbr ).
And in PetClinic tomcat-jdbc is used .
If the application is deployed to Tomcat, you can not include it in war (scope = provided), but at the same time in $ TOMCAT_HOME / lib
It is necessary to put the driver of the database, because the libraries of your war are unavailable from the native tomcat-jdbc.
And of course, when deploying to Tomcat, you shouldn’t forget about the ability to take a pool of connections from
Tomcat configuration context.xml resources:
  <beans profile="jndi"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/javatop"/> </beans> 


Spring Data JPA


Having got used in each project to create our own AbstractDAO, parameterized by the entity and key with the implementation of the main CRUD based on the EntityManager, I was pleased,
that he finally entered Spring, however, into the Spring Data JPA project:
JpaRepository<T, ID extends Serializable>
It is inherited from the more general CrudRepository<T, ID extends Serializable>
from Spring Data Commons .
Working with JPA repositories is amazing first: just write
  public interface UserRepository extends Repository<User, Integer> { User findByEmail(String email); } 

and the method itself will work without a single line of implementation!

Appeal to the original sources showed that the insides of magic - proxying, regexp and reflection:

A rhetorical question for readers: can java be considered a dynamic language :)?

If JpaRepository and the generated methods are not enough, you can write your own implementation of the methods or Query queries .
In @Query you can write JPQL queries (which are generated in @NamedQuery ), or you can refer to already declared @NamedQuery in entities (for some reason in PetClinic @NamedQuery
ignored, although such requests are built and checked at the deployment stage).
For example, the method
  @Modifying @Transactional @Query(name = User.DELETE) int delete(@Param("id") int id); 

refers to the @NamedQuery declared in User
  @NamedQueries({ ... @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id") }) public class User extends NamedEntity { public static final String DELETE = "User.delete"; 

Unlike void CrudRepository.delete(ID id) it will return the number of records modified.

However, there is a problem: the inheritance of the business data access interface from the JpaRepository means that the level of service becomes dependent on implementation.
In addition, for example, in the List<T> findAll(Sort sort) method, the List<T> findAll(Sort sort) class is also located in Spring Data and you do not want to set it in services.
The signature of the methods of the interface becomes bound to signatures in the JpaService . Inconvenient debazitsya and logging. All JpaRepository methods that we don’t need at the service level are JpaRepository in the business interface, or, when inheriting from the org.springframework.data.repository.Repository marker, we don’t have @Override ...
All these problems are solved by another level of delegation:
 public interface ProxyUserRepository extends JpaRepository<User, Integer> { @Modifying @Query("DELETE FROM User u WHERE u.id=?1") @Transactional int delete(int id); @Override @Transactional User save(User user); @Override User findOne(Integer id); @Override List<User> findAll(Sort sort); } @Repository public class DataJpaUserRepository implements UserRepository { private static final Sort SORT_NAME_EMAIL = new Sort("name", "email"); @Autowired private ProxyUserRepository proxy; @Override public boolean delete(int id) { return proxy.delete(id) != 0; } @Override public User save(User user) { return proxy.save(user); } @Override public User get(int id) { return proxy.findOne(id); } @Override public List<User> getAll() { return proxy.findAll(SORT_NAME_EMAIL); } } 

Finally: resources by topic



Spring



Maven



Logging



Persistence




If you like the article, I will prepare Part 2 with the misplaced Spring MVC, Spring Security, Jackson, etc.
Thank you for your attention, it will be interesting to hear your opinion on the topics covered.

Source: https://habr.com/ru/post/232381/


All Articles