JSP + JSTL

One of the most popular ways of creating view layer of java web applications in the past was by using jsp – java server pages. Thay allow you to use mix of static content similar to html with dynamic elements of java code – like scriptlets or tags.

There is some special library of tags created for use in jsp – jstl jsp standard tag library. To use it you should add following dependency in your pom:

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

JSP page could look like that:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Spring CMS</title>
</head>
<body>
	<h1><c:out value="${helloMessage}" /></h1>
	<form action="add" method="post">
		<input name="title" placeholder="Title" /><br/>
		<textarea name="content">Content here...</textarea><br/>
		<input name="tags" placeholder="tag1, tag2, tag3" /><br/>
		<input type="submit" value="Add" />
	</form>
</body>
</html>

or that:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Article added</title>
</head>
<body>
	<c:if test="${not empty article}">
		<h1><c:out value="${article.title}" /></h1>
		<p>
			<c:out value="${article.content}" />
		</p>
		<c:if test="${not empty article.tags}">
			<c:forEach var="tag" items="${article.tags}">
				<li><c:out value="${tag}" /></li>
			</c:forEach>
		</c:if>
	</c:if>
</body>
</html>

Passing information to the view

Usually after receiving some request from client, you would like to deliver him a corresponding response. To do that you could use request attribute:

@PostMapping("/add")
public String add(@RequestParam int a,
                  @RequestParam int b,
                  HttpServletRequest request) {
    int result = a + b;
    request.setAttribute("result", result);
    return "result.jsp";
}

In this case you first retrieve two parameters from the request, execute some operation on them and response with some result, added as the request attribute. This attribute would be reachable from your view layer (e.g. in your jsp file).

Better way to do thar however, would be to use:

@PostMapping("/add")
public String add(@RequestParam int a,
                  @RequestParam int b,
                  Model model) {
    int result = a + b;
    model.addAttribute("result", result);
    return "result.jsp";
}

This version does exactly the same, but is more flexible, as it doesn’t relay on request.

In older version of Spring it was necessary to return object of type View or ModelView, which lead to use the following solution:

@PostMapping("/add")
public ModelAndView add(@RequestParam int a,
                  @RequestParam int b) {
    int result = a + b;
    ModelAndView modelView = new ModelAndView("result.jsp");
    modelView.addObject("result", result);
    return modelView;
}

Nowadays it is possible to return just a Sting with the view name, so this method is no longer in use.

Component scope

In Spring all components by default are singleton. That means, that they are created once, just while application initialization and exist till its finish. Everytime you use this kind of object, you are using the very same one.

In Spring core, there was also an option to switch the bean scope from singleton to prototype – which creates new bean every time you use it. Spring MVC allows you to use beans in three more scopes:

  • request
  • session
  • application

To use beans in those scopes, you should declare it while defining the bean. You can do that by simple using the String, or a final variable from the interface WebApplicationContext.:

@Scope("request")
or
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)

In majority of cases, if you inject bean of a smaller scope into bean of a bigger scope, you should also add the following parameter (depending on the fact, if this bean is implementing any interface or not):

@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.INTERFACES)
or
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)

Dealing with request

By default, annotation @RequestMapping is servicing all types of http requests – GET, POST etc. To serve just one type of request you can add the fallowing parameter to the annotation:

@RequestMapping(path = "/", method = RequestMethod.GET)
or
@RequestMapping(path = "/", method = {RequestMethod.GET, RequestMethod.POST})

or use dedicated annotation instead:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

In your controller class, you can also use methods with argument. Arguments could be passed with different object, and retrieve with use of the following methods:

  • @RequestParam 
  • @RequestHeader 
  • @RequestAttribute 
  • @SessionAttribute 

It could look like that:

@GetMapping("/")
public String home(@RequestParam(name="username", 
                    defaultValue="Stranger",
                    required = true) String username) {
    System.out.println("Hello " + username);
    return "home";
}

When request param and method param have the same name, you can skip it, which leads to easier:

@GetMapping("/")
public String home(@RequestParam(defaultValue="Nieznajomy") String username) {
    System.out.println("Hello " + username);
    return "home";
}

Redirect & forward

When you receive the request, you could work with it on your own, right in the place where it came, or you can also push it somewhere else. There are two ways to do that:

  • using redirect – which shows your client that he is send somewhere else, by changing the address in his browser
  • using forward – which is delegating the request somewhere else without showing it to the customer – the address would stay the same.

Some examples may look like that:

@GetMapping("/")
public String home() {
    return "redirect:another";
}

@GetMapping("/")
public String home() {
    return "forward:another";
}

@GetMapping("/another")
public String another() {
    return "some.jsp";
}

Web App with Spring Boot

To use help of Spring Boot for building your web application you should create a Spring Starter Project with War packaging and Web Dependency.

Spring Boot will automatically create a project structure for you. This time, there will be no need to add WebConfiguration class. All you need to set prefix and suffix for your web files names is the followinh entry into application.properties:

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

Controller and jsp files should be the same as in case application without Spring Boot:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomeController {

    @RequestMapping("/")
    public String home() {
        return "home";
    }
}

webapp/WEB-INF/views/home.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Homepage</title>
</head>
<body>
    <h1>Homepage for Spring app</h1>
</body>
</html>

Application prepared in this way you could:
– Run on Server (Tomcat), and find under: localhost:8080/applicationName/
– Run by starting the main method, but then additional dependency is needed to compile jsp files:

<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
	<scope>provided</scope>
</dependency>

Alternative way of packaging and running web applications

You could also create an app in a form of jar file, using embedded server. In that case it would be impossible to use jsp files, so you need to use just html files for your views. rest of a app stays the same.

When using jar packageing you can run your apps with Run As > Spring Boot App (or Java Application) and find it under localhost:8080 (not localhost:8080/applicationName!).

Spring MVC

To create web application using Spring MVC yoy need to:

  • create maven project, using prototype: maven-archetype-webapp
  • add following dependencies to pom file:
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>5.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
  • if you don’t want to use web.xml file, mark it as optional in project properties: what will let you to delete it:
	<properties>
		<failOnMissingWebXml>false</failOnMissingWebXml>
	</properties>
  • add a location for classes – /src/main/java
  • create configuration class of web application – creating WebApplicationContext and ServletDispatcher (in main catalog):
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class AppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.setServletContext(servletContext);
        DispatcherServlet dispatcher = new DispatcherServlet(ctx);
        
        Dynamic dispatcherConfig = servletContext.addServlet("dispatcher", dispatcher);
        dispatcherConfig.addMapping("/");
    }
}
  • alternatively you can use help from abstract class delivered by Spring, which change configuration class to following form:
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}
  • create home page for your application (in src\main\webapp\WEB-INF\views or similar)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Homepage</title>
</head>
<body>
    <h1>Homepage for Spring app</h1>
</body>
</html>
  • create a controlller of our application (in package like com.projectName.controller)
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomeController {

    @RequestMapping("/")
    public String home() {
        return "home";
    }
    
}

This controller is returning the name of a file, which should be displayed after entering url: localhost:8080/application-name/

To make it work, we must also set its:
– prefix, as src/main/webapp/ is default location for files, but you can put them in some additional folders to make it easier to navigate among them,
– suffix, as the fileName is finished with its extension, but it would be pointless to repeat it in each return statement again and again.

  • configure ViewResolver:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@ComponentScan
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Spring data

When you use Dao for connection with the database you need to declare methods like save, update, get, delete as well as some more individual once. Spring data project was created to make it easier to reach different set of data or execute some actions on them.

Instead of creating Dao interface and implementation for your entities, Spring data delivers interfaces and is able to create the implementation based on them by himself. Those interfaces are:

  • CrudRepository<EntityClass, IdClass>
  • PagingAndSortingRepository <EntityClass, IdClass>

CrudRepository

All you need to do is create an interface that extends one of those generic interfaces, using the EntityClass that you create it for, e.g:

@Repository
public interface CarRepository extends CrudRepository<Car, Long>{} 

And it could be it – no methods needs to be declared, no implementations of a inteface need to be created. Streight from this point CrudRepository is delivering to your CarRepository the following methods:

  • count() – counting records in repository/database
  • deleteById(id) – deletes record with give id,
  • deleteAll(Iterable) – deletes all record represented by entities from given iterable collection,
  • delete(entity) – deletes record represented by given entity,
  • deleteAll() – deletes all records,
  • existsById(id) – checks if record with given id exist,
  • findAll() – return the collection of all records,
  • findAllById(ids) – returns records with ids given as a collection,
  • findById(id) – returns record with given id as Optional class,
  • save(entity) – saves given entity in a database,
  • saveAll(iterable) – saves all entities given as a iterable collection in a database.

You can use all of those methods just from your repository bean retrieved from the context:

        CarRepository carRepo = ctx.getBean(CarRepository.class); 
        Car firstCar = carRepo.findById(1L).get(); //pobieramy pierwszy
        carRepo.delete(firstCar); //usuwamy go

It is possible even through you’ve never created any CarRepositoryImpl class or anything similar – Spring Data does it by itself!

What is more Spring data it able to create more specific queries just by interpretation of method name, which you add to your repository. It is enough to add method signature like:

List<Car> findByBrand(String brand);

To be able to retrieve from the database the collection of cars from given brand. Words find, get, read and query could be use interchangeably, as they all works the same way.

After findBy/findAllBy you can declare any field from the entity. What is more, you can also declare more then one field, using logical operators (And, Or) creating signature like:

List<Car> findAllByBrandAndPrice(String brand, double price);

You can also use the following expresions: like, notlike, startingwith, endingwith, containing, greaterThan/greaterThanEqual, lessThan/lessThanEqual, between, isNull, isNotNull/notNull, orderBy(fieldName)Asc/Desc.

Some examples are:

List<Car> findAllByNameEndingWith(String pattern);
List<Car> findAllByPriceLessThan(double price);
List<Car> findAllByPriceBetween(double low, double high);
List<Car> findAllByBrandOrderByPriceAsc(String brand);

PagingAndSortingRepository

This interface brings us the possibility to retrieve only some part of date, and deliveries two more methods:

  • findAll(pageable) – returns object of class Page with all given entities
  • findAll(sort) – returns zwraca rekordy posortowane zgodnie z przekazanymi kryteriami

To use Spring data you should include the following dependency in your pom:

<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-jpa</artifactId>
	<version>2.0.7.RELEASE</version>
</dependency>

You would also need to:

  • add annotation over your config class, pointing out the location of repositories @EnableJpaRepositories(basePackages = “pl.javastart.repository”)
  • declare the bean, with exactly this signature:
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory (JpaVendorAdapter adapter, DataSource ds) {
    (…)
    }

    @Bean
    public PlatformTransactionManager transactionManager
    (EntityManagerFactory emf){
    (…)
    }

JPQL – SQL for Java

In general cases methods of EntityManager would be sufficient for operation on data. Nevertheless, in some situation you my need more control over queries you sent to the database. For that reason you can declare queries in your application using JPQL – Java Persistence Query Language. JPQL is mostly used for setting some parameters of a query, so to modify the select.

Differences between JPQL and SQL:

  • there is no insert in JPQL – you can only use .persist() method for that
  • JPQL working in the java world, so you use names of entities and fields, not tables and columns
  • JPQL is case sensitive
  • in JPQL you need to use alias of the table name

Examples of JPQL vs SQL sentences:

SQLJPQL
SELECT * FROM city SELECT c FROM City c
SELECT Name FROM city SELECT c.name FROM City c
SELECT * FROM city WHERE Population > 1000000 SELECT c FROM City c WHERE c.population > 1000000
UPDATE City SET Population = Population + 100 WHERE countryCode = 'POL' UPDATE City c SET c.population = c.population + 100 WHERE c.countryCode = 'POL'
DELETE FROM City WHERE countryCode = 'BRA' DELETE FROM City c WHERE c.countryCode = 'BRA'

How to execute queries

To execute queries written in JPQL you need to use features of EntityManager, which delivers you the following classes:

  • Query, which can by obtain as a result of entityManager.createQuery(“<JPQL Query>”);
  • TypedQuery, which can by obtain as a result of entityManager.createQuery(”
    <JPQL Query>”, Result.class).

As it is easy to imagine, query is a general class, that doesn’t allow us to use methods of an entity obtain from database. To do that, we would need to cast the query instance to entity class.

TypedQuery allows us to declare the type of entity even before we will get it from the database, so that we can use its methods from the very beginning.

Now, when we know how to create our JPQL query, we need to execute it. There are three ways to do that:

  • .getSingleResult() – which works for SELECT statements, and can return only one entity as a result – thows error in case of more possible answers from database
  • .getResultList() – which also works for SELECT statements, and can return many entities from database and doesn’t throw any errors
  • .executeUpdate – which works for DELETE and UPDATE statements, and returns the number of changed records.

Example of SELECT statements, when we would like to work with many entities return from th database, ond one of them in particular could look like that:

TypedQuery<EntityName> query = entityManager.createQuery("<JPQL Query>", EntityName.class);
List<EntityName> resultList = query.getResultList();
EntityName entityName= resultList.get(0);

Example of a statement that is suppose to change data in database could look like that:

Query query = em.createQuery("<JPQL Query>");
query.executeUpdate();

Static queries

Defining queries just before executing them can be enough at the beginning. With time it may happen, that you would like to use the same query many times. It this case it would be inefficient to define it each time from the scratch. To make it easier for you (and better for application performance too), you can define a query once, and use it later on. To do that you should use the following annotation above you entity:

@NamedQuery(name = "EntityName.queryName", query = "<JPQL Query>")

To execute such a query in your Dao method, it would be enough to write:

Query query = entityManager.createNamedQuery("EntityName.queryName");
query.executeUpdate();

//    or    //

TypedQuery<EntityName> query = entityManager.createNamedQuery("EntityName.queryName", EntityName.class);
List<EntityName> resultList = query.getResultList();

To define multiple queries in one entity you should use:

@NamedQueries({
    @NamedQuery(name = "EntityName.firstQueryName", query = "<JPQL Query>"),
    @NamedQuery(name = "EntityName.secondQueryName", query = "<JPQL Query>")
})

Parametrized query

You can also use a parameter in your query – a variable that you will specify just before execution of a query. To do that with NamedQuery you should use:

@NamedQuery(name = "EntityName.queryName", query = "SELECT e FROM EntityName e WHERE e.fieldName = :parameterName")

To set the parameter and execute the query you should use:

        TypedQuery<EntityName> query = entityManager.createNamedQuery("EntityName.queryName", EntityName.class);
        query.setParameter("parameterValue", parameterName);
        List<EntityName> resultList = query.getResultList();

Operations on related entities

It is quite common to execute CRUT operations on entities that are related with other entities. It those situations question arise -what should be done to related objects? Should you delete subject object of deleted owner object? It could be also quite demanding to create Daos for all entities, create them one after another, save to the db and then finally create the relations. To make it easier, you can use different CascadeType:

  • CascadeType.PERSIST
  • CascadeType.MERGE 
  • CascadeTyle.REMOVE 
  • CascadeType.REFRESH 
  • CascadeType.DETACH 
  • CascadeType.ALL 

First five of them define which operations on entity should be executed on its related entities as well. They can be used together. Last one stands for all types of operations.

Example of CascadeType declaration:

@OneToMany(mappedBy = "fieldName", 
            fetch = FetchType.EAGER,
            cascade = CascadeType.PERSIST)

By using CascadeType.PERSIST you can create an entity and all its dependencies with only one use of .persist(). But if you would like to create two directional relation between them you need you take care also about adding owner entity to subject entities. To do that you should create such an owner entity method:

public void addSubjectEntity(SubjectEntity subjectEntity ) {
        subjectEntity.setOwnerEntity(this);
        getSubjectEntityField().add(subjectEntity);
    }

Orphan Removal

In some cases deleting the relation between entities is not all that we would like to do. Beside of that we also want to delete the entities that were related to out main entity. To do tht you should use the following attribute

@OneToMany(mappedBy = "client", 
            fetch = FetchType.EAGER,
            cascade = { CascadeType.PERSIST, CascadeType.REMOVE },
            orphanRemoval = true)

Relation types – introduction

It is possible (and very useful in majority of cases) to not only store data in separate tables of database, but also relate them with each other. Thanks to that you don’t have to duplicate the same information in many tables and at the same time you can reach them easily.

In such situation both tables need to have their representation in java word – respective entities. It this part you will find out how you should create the connection (relation) between them, using one of the following relation type:

One to one – if only one raw of table A is related with only one raw of table B. For example exactly one person (name, surname, pesel no.) is related with exactly one national identity card (with its no., release & expiration date) . There is no person with two national identity cards, and no identity card is valid for more then one person. In reality this kind of relation is quite uncommon.

One to Many & Many to One – if one raw from table A could be related with many rows from table B. For example one person can be the owner of many telephone numbers
(with its balance, provider etc.), but one telephone number can by owned only be one person.

Many to many – if many rows of table A can be related with many rows from table B. For example one person can like many movies, and one movie can be liked by many people.

One or two directions of relation

In case of one direction relation only one table holds the information about the respective data from other table. So table A knows where to find related information in table B, but table B doesn’t know that it is related with table A.

In case of two direction relation both tables holds information about respective data from the other table. Table A knows where to find information related with it rows in table B, and table B knows where to find information related with its rows in table A.

Relation type – One to one

In case of one direction relation of type one to one, you need to use an annotation @OneToOne over declaration of field, that represents other table (and entity/class).

Keep in mind that field over which you use annotation is reprezenting only one instance of an entity, so you use it directly as a field type.

The class in which you will add such a field with its annotation is the owner of a relation, and only that class know about it.

What is more, you need to implement some changes in DaoImpl for that entity – to always save changes in both entities – the owner and the subject of relation.

public void save(OwnerEntity ownerEntity){
    SubjectEntity subjectEntity= ownerEntity.getSubjectEntity();
    if(subjectEntity!= null && subjectEntity.getId() == null) {
        entityManager.persist(subjectEntity);
    }
    entityManager.persist(ownerEntity);
}

public void update(OwnerEntity ownerEntity) {
    SubjectEntity subjectEntity= ownerEntity.getSubjectEntity();
    if(subjectEntity!= null) {
        subjectEntity= entityManager.merge(subjectEntity);
        ownerEntity.setSubjectEntity(subjectEntity);
    }
    entityManager.merge(ownerEntity);
}

public void delete(OwnerEntity ownerEntity) {
    OwnerEntity ownerEntityToDelete= entityManager.merge(ownerEntity);
    entityManager.remove(ownerEntityToDelete.getSubjectEntity());
    entityManager.remove(ownerEntityToDelete);
}

// whole article should be written from the respective of entities, not tables as it is now 😦

Keep in mind, that in case of merge you need to also set the changed subject entity in owner entity. This need doesn’t appear in case of persist.

In case of two directional relation, both entities are holding information about their relation. Annotations used above those fields are different though:

Owner entity – should use @OneToOne and @JoinColumn(name = “name”)
where name will be used for column in owner table to store foreign key – primary key of subject entity.

Subject entity – should use @OneToOne(mappedBy = “name”),
where name represent the name of a field (not a column!) in owner entity for subject entity.

// in case of two directional relation there is no need to persist or merge owner and subject class separetly?


Relation type – one to many and many to one

In case of one direction relation you create a relation between one owner entity and many subject entities. That is why in owner class you will declare a field of list or set type, not directly subject class. Subject entities doesn’t know who is heir owner (it is a one direction relation!), so there is no field with the owner entity in them.

You need to use the following annotations in owner entity:
@OneToMany
@JoinColumn(name = “name”,referencedColumnName=”id_entityName”) where:
hibernate will use name for a column with subject entity in owner table and
referencedColumnName points at the name of a column with owner primary key.

In case of two direction relation both side will have fields for each other and will need some annotations. It is worth noticing that in this case the ownership turns around – one of those many subjects is becoming the owner of relation, as it has exactly one subject.

Owner entity will use following annotations:
@ManyToOne
@JoinColumn(name = “name_of_column”)
where name defines the name of a column in owner table with foreign key of subject.

Subject entity will use the following annotations:
@OneToMany(mappedBy=”nameOfField”)
where mappedBy defines the name of a field in owner class, where subject class will be placed.

When Hibernte ask the database for your data, by default he will ask about:

  • both entities – owner and subject of relation when you use owner Dao (with annotation @OneToMany) – tzw. eager loading
  • only subject entity when you use subject Dao (with annotation @ManyToOne) – tzw. lazy loading

This will cause errors if you try to reach owner entity data from subject entity (received from its Dao). To avoid that you could add the following in subject DaoImpl:

    @Override
    public SubjectEntity get(Long key) {
        SubjectEntity subjectEntity = super.get(key);
        subjectEntity.getOwnerEntity().size();
        return subjectEntity;
    }

// what with DaoImpl? does it need the changes like in one to one relation, or not?

Relation type – many to many

// info about additional table in db

In case of one direction relation the only annotation you need to use is
@ManyToOne in owner entity, over a field with list or set of connected subjects.

However, there are some additional annotation that you could use, if you would like to configure the details about database representation of data:

@JoinTable(
name = "additional_table_name",
joinColumns = {@JoinColumn(
    name="add_table_column_name_for_owner_key", 
    referencedColumnName="basic_table_column_name_for_owner_key")},
inverseJoinColumns = {@JoinColumn(
    name="add_table_column_name_for_subject_key", 
    referencedColumnName="basic_table_column_name_for_subject_key ")})

Relation ManyToMany could be one direction only for java world. Database will always see it as two direction.

In case of two direction relation you should use the same annotation in owner entity and following one in subject entity, over list or set of owners entity:
@ManyToMany(mappedBy = “fieldName”)
where fieldName defines in which field you will store the list or set of subjects in owner entity.

In case of Many to Many relation, we may also need one more annotation:
@Fetch(FetchMode.SELECT)
It helps to avoid loading doubled entities of our related objects.

Eager & lazy loading

Way of loading defines how the application will act when reaching for object with relation:
lazy leading – will not load any information about related object, unless you explicitly ask for them, using some of its method
eager loading – will load all information about related object straight with owner object.

By default relation types use following way of loading subject entities:
@OneToOne & @ManyToOne – eager loading
@OneToMany & @ManyToMany – lazy loading

You can always change the default way by using:

@ManyToOne(fetch = FetchType.LAZY)

@ManyToMany(fetch = FetchType.EAGER)

After using such annotation, you no longer need to implement modified get method, that explicitly ask about related object (like subjectEntity.getOwnerEntity().size(); used above).