Get familiar with the Spring Data and the Spring Boot Data features.
Time: 60 minutes (20 minutes per section).
Code Snippet Manager JDBC
You will continue with the Code Snippet Manager code, but this time using a persistence engine. You will reuse the code from previous labs.
-
Open a browser and hit the url: http://start.spring.io
-
Click the Switch to the full version link.
-
Fill out the Code Snippet Manager JDBC Project metadata with (See Figure 1.0):
Table 1. Code Snippet Manager JDBC App - metadata Property Value Group:
io.pivotal.workshop
Artifact:
code-snippet-manager-jdbc
Name:
code-snippet-manager-jdbc
Package Name:
io.pivotal.workshop.snippet
Dependencies:
Web, DevTools, Groovy Templates, JDBC, H2, MySQL
Spring Boot:
2.0.0.M7
Figure 1.0: Spring Initializr - http://start.spring.io/TipYou can choose either Maven or Gradle project types. -
Type Web, DevTools, Groovy Templates, JDBC, H2, and MySQL in the Dependencies field and press Enter.
-
Click the Generate Project button.
-
Unzip the file in any directory you want.
-
Import your project in any IDE you want.
-
Copy all the packages (with code) into the new project.
-
The classes in io.pivotal.workshop.snippet.domain: Code, Language and Snippet should be the same.
-
Create a new class CrossSnippetLanguageCode in the domain package:
package io.pivotal.workshop.snippet.domain; public class CrossSnippetLanguageCode { private String snippetId; private String languageId; private String codeId; public CrossSnippetLanguageCode() { } public CrossSnippetLanguageCode(String snippetId, String languageId, String codeId) { this.snippetId = snippetId; this.languageId = languageId; this.codeId = codeId; } public String getSnippetId() { return snippetId; } public void setSnippetId(String snippetId) { this.snippetId = snippetId; } public String getLanguageId() { return languageId; } public void setLanguageId(String languageId) { this.languageId = languageId; } public String getCodeId() { return codeId; } public void setCodeId(String codeId) { this.codeId = codeId; } }
this class will hold the relationship between the Code and Language within the Snippet class.
-
Because you will reuse the SimpleRepository interface, is necessary modify all the implementations. Modify the code accordingly:
io.pivotal.workshop.snippet.repository.LanguageRepository.javapackage io.pivotal.workshop.snippet.repository; import java.sql.PreparedStatement; import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import io.pivotal.workshop.snippet.domain.Language; import io.pivotal.workshop.snippet.repository.mapper.LanguageRowMapper; @Repository public class LanguageRepository implements SimpleRepository<Language>{ private final String SQL_FIND_ALL = "select * from language"; private final String SQL_FIND_ONE = "select * from language where name = ?"; private final String SQL_INSERT = "insert into language(id,name,syntax) values(?,?,?)"; private final String SQL_UPDATE = "update language set name = ?, syntax = ? where id = ?"; private JdbcTemplate jdbcTemplate; @Autowired public LanguageRepository(JdbcTemplate jdbcTemplate){ this.jdbcTemplate = jdbcTemplate; } @Override public Iterable<Language> findAll(){ return this.jdbcTemplate.query(SQL_FIND_ALL, new LanguageRowMapper()); } @Override public void saveAll(Collection<Language> languages){ languages.forEach( lang -> saveAll(lang) ); } @Override public Language findById(String name) { try { return this.jdbcTemplate.queryForObject(SQL_FIND_ONE, new Object[]{name}, new LanguageRowMapper()); } catch (EmptyResultDataAccessException ex){ return null; } } @Override public Language saveAll(Language item) { assert item.getName() != null; Language language = this.findById(item.getName()); if(language == null) { this.jdbcTemplate.update( psc -> { PreparedStatement ps = psc.prepareStatement(SQL_INSERT); ps.setString(1, item.getId()); ps.setString(2, item.getName()); ps.setString(3, item.getSyntax()); return ps; }); return item; }else { this.jdbcTemplate.update( psc -> { PreparedStatement ps = psc.prepareStatement(SQL_UPDATE); ps.setString(1, item.getName()); ps.setString(2, item.getSyntax()); ps.setString(3, item.getId()); return ps; }); return language; } } }
io.pivotal.workshop.snippet.repository.CodeRepository.javapackage io.pivotal.workshop.snippet.repository; import java.sql.PreparedStatement; import java.util.Collection; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import io.pivotal.workshop.snippet.domain.Code; import io.pivotal.workshop.snippet.repository.mapper.CodeRowMapper; @Repository public class CodeRepository implements SimpleRepository<Code> { private final String SQL_FIND_ALL = "select * from code"; private final String SQL_FIND_ONE = "select * from code where id = ?"; private final String SQL_INSERT = "insert into code(id,source) values(?,?)"; private final String SQL_UPDATE = "update code set source = ? where id = ?"; private JdbcTemplate jdbcTemplate; @Autowired public CodeRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public List<Code> findAll() { return this.jdbcTemplate.query(SQL_FIND_ALL, new CodeRowMapper()); } @Override public void saveAll(Collection<Code> items) { items.forEach(code -> this.saveAll(code)); } @Override public Code findById(String id) { try { return this.jdbcTemplate.queryForObject(SQL_FIND_ONE, new Object[] { id }, new CodeRowMapper()); } catch (EmptyResultDataAccessException ex) { return null; } } @Override public Code saveAll(Code item) { assert item.getSource() != null; Code code = this.findById(item.getId()); if (code == null) { this.jdbcTemplate.update(psc -> { PreparedStatement ps = psc.prepareStatement(SQL_INSERT); ps.setString(1, item.getId()); ps.setString(2, item.getSource()); return ps; }); return item; } else { this.jdbcTemplate.update(psc -> { PreparedStatement ps = psc.prepareStatement(SQL_UPDATE); ps.setString(1, item.getSource()); ps.setString(2, item.getId()); return ps; }); return code; } } }
io.pivotal.workshop.snippet.repository.SnippetRepository.javapackage io.pivotal.workshop.snippet.repository; import java.sql.PreparedStatement; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import io.pivotal.workshop.snippet.domain.CrossSnippetLanguageCode; import io.pivotal.workshop.snippet.domain.Snippet; import io.pivotal.workshop.snippet.repository.mapper.SnippetRowMapper; @Repository public class SnippetRepository implements SimpleRepository<Snippet> { private final String SQL_FIND_ALL = "select s.*,l.id lang_id, l.name lang_name, l.syntax lang_syntax, c.id code_id, c.source code_source from cross_snippet_language_code cx " + "inner join snippet s on cx.snippet_id = s.id " + "inner join language l on cx.language_id = l.id " + "inner join code c on cx.code_id = c.id "; private final String SQL_FIND_ONE = SQL_FIND_ALL + " where s.id = ?"; private final String SQL_INSERT = "insert into snippet(id,title, keywords, description, created, modified) values(?,?,?,?,?,?)"; private final String SQL_UPDATE = "update snippet set title = ?, keywords = ?, description = ?, modified = ? where id = ?"; private JdbcTemplate jdbcTemplate; private LanguageRepository langRepo; private CodeRepository codeRepo; private CrossSnippetLanguageCodeRepository crossRepo; @Autowired public SnippetRepository(JdbcTemplate jdbcTemplate, LanguageRepository langRepo, CodeRepository codeRepo, CrossSnippetLanguageCodeRepository crossRepo) { this.jdbcTemplate = jdbcTemplate; this.langRepo = langRepo; this.codeRepo = codeRepo; this.crossRepo = crossRepo; } @Override public List<Snippet> findAll() { return this.jdbcTemplate.query(SQL_FIND_ALL, new SnippetRowMapper()); } @Override public void saveAll(Collection<Snippet> items) { items.forEach(code -> this.saveAll(code)); } @Override public Snippet findById(String id) { try { return this.jdbcTemplate.queryForObject(SQL_FIND_ONE, new Object[] { id }, new SnippetRowMapper()); } catch (EmptyResultDataAccessException ex) { return null; } } @Override public Snippet saveAll(final Snippet item) { assert item.getId() != null; assert item.getTitle() != null; assert item.getLang() != null; assert item.getCode() != null; Snippet snippet = this.findById(item.getId()); Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); if (snippet == null) { this.crossRepo.saveAll(new CrossSnippetLanguageCode(item.getId(), item.getLang().getId(), item.getCode().getId())); this.langRepo.saveAll(item.getLang()); this.codeRepo.saveAll(item.getCode()); this.jdbcTemplate.update(psc -> { PreparedStatement ps = psc.prepareStatement(SQL_INSERT); ps.setString(1, item.getId()); ps.setString(2, item.getTitle()); ps.setString(3, item.getKeywords()); ps.setString(4, item.getDescription()); ps.setString(5, dateFormat.format(date)); ps.setString(6, dateFormat.format(date)); return ps; }); return item; } else { this.crossRepo.saveAll(new CrossSnippetLanguageCode(snippet.getId(), snippet.getLang().getId(), snippet.getCode().getId())); this.langRepo.saveAll(item.getLang()); this.codeRepo.saveAll(item.getCode()); this.jdbcTemplate.update(psc -> { PreparedStatement ps = psc.prepareStatement(SQL_UPDATE); ps.setString(1, item.getTitle()); ps.setString(2, item.getKeywords()); ps.setString(3, item.getDescription()); ps.setString(4, dateFormat.format(date)); ps.setString(5, item.getId()); return ps; }); return snippet; } } }
Every single class is using the JdbcTemplate (with some methods like: queryForObject, query and update) and the RowMapper, analyze the code and review the SQL statements.
-
As you already guess from the code above, the SnippetRepository class has on its constructor the CrossSnippetLanguageCodeRepository reference. Create this class:
io.pivotal.workshop.snippet.repository.CrossSnippetLanguageCodeRepository.javapackage io.pivotal.workshop.snippet.repository; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import io.pivotal.workshop.snippet.domain.CrossSnippetLanguageCode; @Repository public class CrossSnippetLanguageCodeRepository implements SimpleRepository<CrossSnippetLanguageCode> { private final String SQL_FIND_ALL = "select * from cross_snippet_language_code"; private final String SQL_FIND_ONE = "select * from cross_snippet_language_code where snippet_id = ?"; private final String SQL_INSERT = "insert into cross_snippet_language_code(snippet_id,language_id,code_id) values(?,?,?)"; private final String SQL_UPDATE = "update cross_snippet_language_code set langauge_id = ?, code_id = ? where snippet_id = ?"; private JdbcTemplate jdbcTemplate; private final RowMapper<CrossSnippetLanguageCode> rowMapper = (ResultSet rs, int row) -> { CrossSnippetLanguageCode cross = new CrossSnippetLanguageCode(); cross.setSnippetId(rs.getString("snippet_id")); cross.setLanguageId(rs.getString("language_id")); cross.setCodeId(rs.getString("code_id")); return cross; }; @Autowired public CrossSnippetLanguageCodeRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public Iterable<CrossSnippetLanguageCode> findAll() { return this.jdbcTemplate.query(SQL_FIND_ALL, rowMapper); } @Override public void saveAll(Collection<CrossSnippetLanguageCode> items) { items.forEach(cross -> { this.saveAll(cross); }); } @Override public CrossSnippetLanguageCode saveAll(CrossSnippetLanguageCode item) { assert item.getSnippetId() != null; assert item.getLanguageId() != null; assert item.getCodeId() != null; CrossSnippetLanguageCode cross = this.findById(item.getSnippetId()); if (cross == null) { this.jdbcTemplate.update(psc -> { PreparedStatement ps = psc.prepareStatement(SQL_INSERT); ps.setString(1, item.getSnippetId()); ps.setString(2, item.getLanguageId()); ps.setString(3, item.getCodeId()); return ps; }); return item; } else { this.jdbcTemplate.update(psc -> { PreparedStatement ps = psc.prepareStatement(SQL_UPDATE); ps.setString(1, item.getLanguageId()); ps.setString(2, item.getCodeId()); ps.setString(3, item.getSnippetId()); return ps; }); return cross; } } @Override public CrossSnippetLanguageCode findById(String id) { try { return this.jdbcTemplate.queryForObject(SQL_FIND_ONE, new Object[] { id }, rowMapper); } catch (EmptyResultDataAccessException ex) { return null; } } }
Have you noticed that the CrossSnippetLanguageCodeRepository is using a declared Java 8 lambda notation for the RowMapper?
-
Talking about RowMapper, all the above code is using it to map the ResultSet with the domain class. Create the following mappers in the io.pivotal.workshop.snippet.repository.mapper package:
io.pivotal.workshop.snippet.repository.mapper.LanguageRowMapper.javapackage io.pivotal.workshop.snippet.repository.mapper; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; import io.pivotal.workshop.snippet.domain.Language; public class LanguageRowMapper implements RowMapper<Language> { @Override public Language mapRow(ResultSet rs, int rowNum) throws SQLException { Language lang = new Language(); lang.setId(rs.getString("id")); lang.setName(rs.getString("name")); lang.setSyntax(rs.getString("syntax")); return lang; } }
io.pivotal.workshop.snippet.repository.mapper.CodeRowMapper.javapackage io.pivotal.workshop.snippet.repository.mapper; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; import io.pivotal.workshop.snippet.domain.Code; public class CodeRowMapper implements RowMapper<Code> { @Override public Code mapRow(ResultSet rs, int rowNum) throws SQLException { Code code = new Code(); code.setId(rs.getString("id")); code.setSource(rs.getString("source")); return code; } }
io.pivotal.workshop.snippet.repository.mapper.SnippetRowMapper.javapackage io.pivotal.workshop.snippet.repository.mapper; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; import io.pivotal.workshop.snippet.domain.Code; import io.pivotal.workshop.snippet.domain.Language; import io.pivotal.workshop.snippet.domain.Snippet; public class SnippetRowMapper implements RowMapper<Snippet> { @Override public Snippet mapRow(ResultSet rs, int rowNum) throws SQLException { Language lang = new Language(); lang.setId(rs.getString("lang_id")); lang.setName(rs.getString("lang_name")); lang.setSyntax(rs.getString("lang_syntax")); Code code = new Code(); code.setId(rs.getString("code_id")); code.setSource(rs.getString("code_source")); Snippet snippet = new Snippet(); snippet.setId(rs.getString("id")); snippet.setTitle(rs.getString("title")); snippet.setDescription(rs.getString("description")); snippet.setKeywords(rs.getString("keywords")); snippet.setCreated(rs.getDate("created")); snippet.setModified(rs.getDate("modified")); snippet.setLang(lang); snippet.setCode(code); return snippet; } }
-
The SnippetController and the SnippetConfiguration classes must be the same. Practically there is no change on any of them, they should work.
-
Next add the schema.sql in the src/main/resources/ folder.
DROP TABLE IF EXISTS snippet; CREATE TABLE snippet ( id varchar(36) NOT NULL, title varchar(200) NOT NULL, keywords varchar(250) DEFAULT NULL, description varchar(500) DEFAULT NULL, created date NOT NULL, modified date NOT NULL, PRIMARY KEY (id) ); DROP TABLE IF EXISTS language; CREATE TABLE language ( id varchar(36) NOT NULL, name varchar(250) NOT NULL, syntax varchar(250) DEFAULT NULL, PRIMARY KEY (id) ); DROP TABLE IF EXISTS code; CREATE TABLE code ( id varchar(36) NOT NULL, source varchar(5000) NOT NULL, PRIMARY KEY (id) ); DROP TABLE IF EXISTS cross_snippet_language_code; CREATE TABLE cross_snippet_language_code ( snippet_id varchar(36) NOT NULL, language_id varchar(36) NOT NULL, code_id varchar(36) NOT NULL, PRIMARY KEY (snippet_id) );
Tip
|
Before you run your application make sure to have copied resources/static and resources/templates files from the previous labs. |
Challenges
-
Run the application and make sure it works.
-
Do any necessary change to have the home page working.
Figure 1.1: Code Snippet Manager JDBC - http://localhost:8080/
Code Snippet Manager JPA
You will continue with the Code Snippet Manager code, but this time using JPA. You will reuse the code from previous labs.
-
Open a browser and hit the url: http://start.spring.io
-
Click the Switch to the full version link.
-
Fill out the Code Snippet Manager JPA Project metadata with (See Figure 2.0):
Table 2. Code Snippet Manager JPA App - metadata Property Value Group:
io.pivotal.workshop
Artifact:
code-snippet-manager-jpa
Name:
code-snippet-manager-jpa
Package Name:
io.pivotal.workshop.snippet
Dependencies:
Web, DevTools, Groovy Templates, JPA, H2, MySQL
Spring Boot:
2.0.0.M7
Figure 2.0: Spring Initializr - http://start.spring.io/TipYou can choose either Maven or Gradle project types. -
type Web, DevTools, Groovy Templates, JPA, H2, and MySQL in the Dependencies field and press Enter.
-
Click the Generate Project button.
-
Unzip the file in any directory you want.
-
Import your project in any IDE you want.
-
Copy all the packages (with code) into the new project.
-
The classes in io.pivotal.workshop.snippet.domain: Code, Language and Snippet will change because you will use the JPA features. Create/modify the following classes:
io.pivotal.workshop.snippet.domain.Language.javapackage io.pivotal.workshop.snippet.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; @Entity public class Language { @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") private String id; private String name; private String syntax = "text"; public Language() { } public Language(String name) { this(); this.name = name; } public Language(String name, String syntax) { this(name); this.syntax = syntax; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSyntax() { return syntax; } public void setSyntax(String syntax) { this.syntax = syntax; } }
io.pivotal.workshop.snippet.domain.Code.javapackage io.pivotal.workshop.snippet.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; @Entity public class Code { @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") private String id; private String source; public Code() { } public Code(String source) { this(); this.source = source; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } }
io.pivotal.workshop.snippet.domain.Snippet.javapackage io.pivotal.workshop.snippet.domain; import java.util.Date; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import org.hibernate.annotations.GenericGenerator; @Entity public class Snippet { @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") private String id; private String title; private String keywords = ""; private String description = ""; @OneToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL) private Language lang; @OneToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL) private Code code; @Column(insertable = true, updatable = false) private Date created; private Date modified; public Snippet() { this.id = java.util.UUID.randomUUID().toString().replaceAll("-", ""); this.created = new Date(); this.modified = new Date(); } public Snippet(String title, String keywords, String description, Language lang, Code code) { this(); this.title = title; this.keywords = keywords; this.description = description; this.lang = lang; this.code = code; } public Snippet(String title, Language lang, Code code) { this(title, "", "", lang, code); } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Language getLang() { return lang; } public void setLang(Language lang) { this.lang = lang; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getKeywords() { return keywords; } public void setKeywords(String keywords) { this.keywords = keywords; } public Code getCode() { return code; } public void setCode(Code code) { this.code = code; } public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } public Date getModified() { return modified; } public void setModified(Date modified) { this.modified = modified; } @PrePersist void onCreate() { this.setCreated(new Date()); this.setModified(new Date()); } @PreUpdate void onUpdate() { this.setModified(new Date()); } }
See that you are using now the JPA standard annotations: @Entity, @Id, @OneToOne, etc.
-
The repositories will change becasue you will use the power of the spring-data and spring-data-jpa projects. Create/Modify the following classes:
io.pivotal.workshop.snippet.repository.LanguageRepository.javapackage io.pivotal.workshop.snippet.repository; import org.springframework.data.repository.CrudRepository; import io.pivotal.workshop.snippet.domain.Language; public interface LanguageRepository extends CrudRepository<Language, String> { }
io.pivotal.workshop.snippet.repository.CodeRepository.javapackage io.pivotal.workshop.snippet.repository; import org.springframework.data.repository.CrudRepository; import io.pivotal.workshop.snippet.domain.Code; public interface CodeRepository extends CrudRepository<Code, String> { }
io.pivotal.workshop.snippet.repository.SnippetRepository.javapackage io.pivotal.workshop.snippet.repository; import org.springframework.data.repository.CrudRepository; import io.pivotal.workshop.snippet.domain.Snippet; public interface SnippetRepository extends CrudRepository<Snippet, String> { }
As you can see from the code above, you don’t need to implement anything but just extend from the CrudRepository<T,ID> interface, which uses generics, meaning that you need to pass the domain class and the unique identifies, in this case what is marked as @Id. All the operation from the CrudRepository<T,ID> interface will be implemented by the spring-data classes.
-
The SnippetController and the SnippetConfiguration classes must be the same. Practically there is no change on any of them, they should work.
-
This time there is no need for any SQL schema, this time you will use the DDL auto-creation feature. In the src/main/resources/application.properties file add the following content:
src/main/resources/application.properties## JPA spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=create-drop
Tip
|
Before you run your application make sure to have copied resources/static and resources/templates files from the previous labs. |
Challenges
-
Run the application and make sure it works.
-
Do any necessary change to have the home page working.
Figure 1.1: Code Snippet Manager JPA - http://localhost:8080/
Code Snippet Manager JPA REST
You will continue with the Code Snippet Manager code, but this time using Data Rest module. You will reuse the code from previous labs.
-
Open a browser and hit the url: http://start.spring.io
-
Click the Switch to the full version link.
-
Fill out the Code Snippet Manager JPA REST Project metadata with (See Figure 3.0):
Table 3. Code Snippet Manager JPA REST App - metadata Property Value Group:
io.pivotal.workshop
Artifact:
code-snippet-manager-jpa-rest
Name:
code-snippet-manager-jpa-rest
Package Name:
io.pivotal.workshop.snippet
Dependencies:
Web, DevTools, Groovy Templates, JPA, Rest Repositories, H2, MySQL
Spring Boot:
2.0.0.M7
Figure 3.0: Spring Initializr - http://start.spring.io/TipYou can choose either Maven or Gradle project types. -
type Web, DevTools, Groovy Templates, JPA, Rest Repositories, H2, and MySQL in the Dependencies field and press Enter.
-
Click the Generate Project button.
-
Unzip the file in any directory you want.
-
Import your project in any IDE you want.
-
Copy all the packages (with code) into the new project.
-
The only class that will change will be the SnippetController. Create/modify the folowing class:
io.pivotal.workshop.snippet.controller.SnippetController.javapackage io.pivotal.workshop.snippet.controller; import java.util.HashMap; import java.util.Map; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import io.pivotal.workshop.snippet.repository.LanguageRepository; import io.pivotal.workshop.snippet.repository.SnippetRepository; @RestController public class SnippetController { private SnippetRepository snippetRepository; private LanguageRepository languageRepository; public SnippetController(SnippetRepository snippetRepository,LanguageRepository languageRepository){ this.snippetRepository = snippetRepository; this.languageRepository = languageRepository; } @RequestMapping("/") public ModelAndView home(){ assert snippetRepository != null; Map<String,Object> model = new HashMap<String,Object>(); model.put("langs", languageRepository.findAll()); model.put("snippets", snippetRepository.findAll()); return new ModelAndView("views/home",model); } }
As you can see, only is necessary the home method, and this is because the spring-data-rest module will take care of creating all the web controllers for accepting any request regarding to the domain repositories.
-
When using the spring-data-rest libraries, the default path for the controllers is the root: /, but in this case you are using the root as home page, that’s why is necessary to override the spring-data-rest context path defaults, in the src/main/resources/application.properties file add the following:
src/main/resources/application.properties## JPA spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=create-drop ## REST spring.data.rest.base-path=api
Now you should be able to use the http://localhost:8080/api to access the REST repositories.
Tip
|
Before you run your application make sure to have copied resources/static and resources/templates files from the previous labs. |
Challenges
-
Run the application and make sure it works.
TipSee the logs and take a look at all the mapping that is being generated. -
Do any necessary change to have the home page working.
Figure 3.1: Code Snippet Manager JPA REST - http://localhost:8080/ -
Go to the http://localhost:8080/api, you should see the following image:
Figure 3.2: Code Snippet Manager JPA REST - http://localhost:8080/apiand if you click the http://localhost:8080/api/snippets you should see the following image:
Figure 3.2: Code Snippet Manager JPA REST - http://localhost:8080/api/snippets -
Review all the links and see that the all the information is being translated into a JSON/HAL.
-
So far we are using the H2 engine, do the necessary changes to use now MySQL, how can you accomplish this?
HOMEWORK
-
Use multiple databases like MySQL and MongoDB (multiple Datasources).
-
Use Flyway or Liquibase to do a Database migration. Add an executeCommand field to the Code domain class. This field will tell how to execute the source code provided.
-
Use jOOQ framework.