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

  • Tutorial

Hello!
Spring MVC , according to the 2014 RevbelLabs Java Tools and Technology Review , is the most popular web framework.
Further, the same review calls the leader of ORM - Hibernate and the leader of web containers - Apache Tomcat. Add here the jQuery most used java script library , the most popular Bootstrap css framework , the most popular Maven build tool (not against Gradle's attack) , the absolute leader among the JUnit test frameworks, and we will get an example application on Spring from its creators:
Spring Pet Clinic ( demo application ).
In addition to the above, 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 are also included in this rather simple functionality project .

Progress in software development implies a reduction in the volume of the application’s own code, ideally, only to the business logic of the application.
However, this is not free - the number of dependencies has exceeded fifty for even a simple project (there are 61 jar in PetClinic in WEB-INF \ lib).
Of course do not necessarily know all of them, some jar catching up in the background, and we are not even aware of them until you see the finished war or not
feasible mvn project-info-reports:dependencies(in IDEA: Show Dependencies ... in the Maven project). But you have to work with the main ones. And
sometimes it takes hours, or even days, to fight some of their features. And you have to deal with the bugs of the frameworks themselves ...

Recently, inspired by Pet Clinic, when creating a webinar on these technologies, I created the Todo Management List application: managing a to-do list
with authorization and user registration. Pet Clinic dependencies added moreSpring Security / the very recent Spring Security
Test
and
jQuery Jeditable and jQuery notification plugins .
The volume of the article does not allow us to describe the steps of creating an application (a webinar on creating an application takes 30 hours),
so here I share resources, some thoughts and solutions that came in the process of creating it.
On PaaS Heroku you can find a demo of the application (the first
time you start it can take a long load and server error, repeat).

Application examples


On the Internet, there are a lot of applications built on Spring / JPA / MVC / Security. You can download sorts and choose the solution that suits you best.

Spring namespace configuration


There is a tendency in Spring configuration to hide implementation details under its namespaces.
The configuration becomes smaller and more understandable, however, the process of customization or debugging is not quite trivial: first you need to find the beans
where it is implemented.
Compare for example the initialization of the
database
:
classpath*:db/${jdbc.initLocation}classpath*:db/populateDB.sql

and

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

@Transactional in tests


It is customary to use transactionality in Spring tests: after each test is completed, the database rollbacks to its original state.
However, @Transactional itself strongly influences the behavior of tests: for example, you forgot in the @Transactional service / repository, the test passed, and the application crashed.
Even worse, when the test gets entities from the database for comparison:
they fall into the same transactional context and the behavior of the tested methods becomes somewhat different (only evict or detach saves).
The base state during the test debug is also not displayed until the test transaction is completed.
It is more honest to use the base initializer before each test:


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: Spring 4.1 introduced an annotation that replaces DbPopulator:
@org.springframework.test.context.jdbc.Sql

Setting EntityManagerFactory


Accustomed to bugs in Spring 3.0 about undeclared entities in persistence.xml, I was surprised that everything works without it!
After some digging in the code, I saw that all target / classes are scanned for entity annotations. Also pleased with the ability to configure JPA without persistence.xml.
You can specify specific packages for scanning the model, configure provider-specific and general JPA parameters.
And they can be moved to a common db.properties file:

            -->${hibernate.format_sql}${hibernate.use_sql_comments}

Choosing a connection pool implementation.


The traditional choice of implementing DataSource Commons DBCP seems to be losing ground.
According to StackOverflow, for implementation you need to take BoneCP , which is used in playframework (if you are already using it or are planning to,
note that some efforts are required to avoid memory leaks voiced in the report from the Plumbr developer ).
And PetClinic uses tomcat-jdbc .
If the application is deployed in Tomcat, you can not include it in war (scope = provided), but at the same time
you need to put the base driver in $ TOMCAT_HOME / lib , because the libraries of your war are not available from the native tomcat-jdbc.
And of course, when deploying to Tomcat, do not forget about the ability to take the connection pool from the
context.xml resources of the Tomcat configuration:


Spring data jpa


Accustomed in each project to create its own AbstractDAO, parameterized by the entity and key with the implementation of the main CRUDs based on EntityManager, he was glad that
he finally entered Spring, however, in the Spring Data JPA project: It inherits from the more general of Spring Data Commons . Working with JPA repositories is amazing at first: just write
JpaRepository
CrudRepository


    public interface UserRepository extends Repository {
       User findByEmail(String email);
    }

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

An appeal to the primary sources showed that the insides of magic are proxing, regexp and reflection:
  • first, the interface is JpaRepositoryFactory.getRepositoryBaseClassproxied in one of the implementations: QueryDslJpaRepository(when using
    Unified Queries for Java ) orSimpleJpaRepository
  • then all methods are analyzed - candidates for Query ( DefaultRepositoryInformation.isQueryMethodCandidate).
    Simplified, everything with an @Queryannotation and everything that is not in gets there JpaRepository;
  • then the method names are parsed in PartTree via
        PartTree.PREFIX_TEMPLATE: Pattern.compile("^(find|read|get|count|query)(\\p{Lu}.*?)??By")
    

    and seeks correspondence with the essence of the property;
  • finally, the method is trivially implemented through the JPA Criteria API .

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

If the JpaRepository and the generated methods are not enough, you can write your own implementation of the methods or Query queries .
You @Querycan write in JPQL queries (which are generated in @NamedQuery), or you can refer to those already declared @NamedQueryin entities (for some reason, they are @NamedQuery
ignored in PetClinic , although such queries 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 declared in User @NamedQuery
    @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, it will return the number of modified records.

However, there is a problem: the inheritance of the business data access interface from JpaRepositorymeans that the service level becomes implementation dependent.
In addition, for example, in the method, the Sort class is also located in Spring Data and you do not want to set it in services. The signature of the interface methods becomes attached to the signatures in . It’s inconvenient to debase and log in. All methods that we don’t need at the service level at all, or when inheriting from the marker , we don’t have a check, get to the business interface ... Another level of delegation solves all these problems:List findAll(Sort sort)
JpaServiceJpaRepositoryorg.springframework.data.repository.Repository@Override

public interface ProxyUserRepository extends JpaRepository {
    @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 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 getAll() {
    return proxy.findAll(SORT_NAME_EMAIL);
    }
}

Lastly: Resources by topic



Spring



Maven



Logging



Persistence




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

Also popular now: