Update:
Thanks all for your answer. I still have problems, since it's not returning the result I hoped. Is it a problem of the placement of the files? They are organized like this: i63.tinypic.com/2by8g2.png
Thanks again in advance.
I'm studying in order to create a web application using Spring Boot and JDBC (without JPA); I'm having several problems doing it.
I don't understand how to correctly create the controller, and how to make it return the .jsp pages that I want.
I searched lots and lots solutions, but just can't find the right one.
This is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>---myprojectname</groupId>
<artifactId>tesieasy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>tesieasy</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
My application.properties:
> spring.h2.console.enabled=true
server.port=8000
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp
My ServetInizializer:
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TesieasyApplication.class);
}
}
My entity:
import java.util.Date;
import java.text.SimpleDateFormat;
public class Journal {
private Long id;
private String title;
private Date created;
private String summary;
private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
/** costruttori**/
public Journal() {}
public Journal(Long id, String title, String summary, Date date){
this.id = id;
this.title = title;
this.summary = summary;
this.created = date;
}
/** getters and setters omitted **/
My service:
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import name of my package-.entity.Journal;
#Service
public class JournalService {
private static final Logger log = LoggerFactory.getLogger(JournalService.class);
#Autowired
JdbcTemplate jdbcTemplate;
public void insertData() {
log.info("> Table creation");
jdbcTemplate.execute("DROP TABLE JOURNAL IF EXISTS");
jdbcTemplate
.execute("-cut for brevity-");
log.info("> Done.");
}
public List<Journal> findAll() {
List<Journal> entries = new ArrayList<>();
jdbcTemplate
.query("SELECT * FROM JOURNAL",
new Object[] {}, (rs, row) -> new Journal(rs.getLong("id"), rs.getString("title"),
rs.getString("summary"), new Date(rs.getTimestamp("created").getTime())))
.forEach(entry -> entries.add(entry));
return entries;
}
public int countEntries() {
int rowCount = this.jdbcTemplate.queryForObject("SELECT count(*) FROM JOURNAL", Integer.class);
return rowCount;
}
public Journal insertJournal() {
Journal journal = null;
this.jdbcTemplate.update(
-cut for brevity-");
return journal;
}
}
One of my .jsp pages:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>A title</title></head>
<body>
Hello! JSP is working fine!
</body>
</html>
And finally my controller:
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import *my package name.entity.Journal;
import *my package name.service.JournalService;
#RestController
public class HelloController {
#Autowired
private JournalService journalService;
#RequestMapping(value="/")
public String home() {
return "home";
}
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
#RequestMapping("/findAll")
public String index() {
List<Journal> all;
all = journalService.findAll();
return all.toString();
}
#RequestMapping("/count")
public int countElements() {
int count;
count = journalService.countEntries();
return count;
}
#RequestMapping("/insert")
public Journal insertElements() {
Journal insert;
insert = journalService.insertJournal();
return insert;
}
}
What I understood:
I don't need a web.xml file
How to connect the service with the controller (even if my findAll method return my data into {}, and I have to understand how to write it properly)
What I don't understand:
Why it's not linking correctly to my JSP pages - what do I need?
Do I need a Model and View?
I hope you can give me some advice and/or some examples right for my scope.
Thanks in advance and best regards!
Solution:
Step1: change the #RestController to #Controller
Step2: for URL that you want to return a view (e.g. greeting.jsp)
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
Step3: for URL that you want to return a JSON object, no view (perhaps an AJAX call)
#RequestMapping("/findAll")
#ResponseBody
public List<Journal> index() {
List<Journal> all = journalService.findAll()
return all;
}
OR this
#RequestMapping("/findAll")
#ResponseBody
public Model index(Model model) {
List<Journal> all =journalService.findAll();
model.addAttribute("journalList", all);
return model;
}
Step4: for URL that you want the view and the model (e.g. a java object)
ref Rob Baily sample code
#RequestMapping("/findAll")
public String index(Model model) {
List<Journal> all;
all = journalService.findAll();
model.addAttribute("journalList", all);
return "listall";
}
and you can access the object in your listall.jsp by using ${journalList}
Explanation:
When you use #RestController on your class, it is equivalent to adding #ResponseBody for every method in your class. Thus, it will always return an object.toString() on the page and not linking to your JSP.
This code uses Spring 4’s new #RestController annotation, which marks the class as a controller where every method returns a domain object instead of a view. It’s shorthand for #Controller and #ResponseBody rolled together.
ref: https://spring.io/guides/gs/rest-service/
jsp sample reference: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-web-jsp
As the comment points out RestController is not correct if you want to return a view to be rendered. A RestController returns the object as JSON rather that an id for a view which is used for AJAX requests. Just use the Controller annotation if you want to return a view.
You do not need a View as it should route the return value to the JSP page. If you want to use JSP tags then yes you will need a Model and you will need to add objects to your Model object. See the example below. The first method should go to welcome.jsp and have no model available. The second method should go to listall.jsp and make a journalList attribute available for use in JSP tags.
#Controller
public class ApplicationController {
#RequestMapping("/")
public String index() {
return "welcome";
}
#RequestMapping("/findAll")
public String index(Model model) {
List<Journal> all;
all = journalService.findAll();
model.addAttribute("journalList", all);
return "listall";
}
}
Related
I'm struggling with SpringMVC configurations based on java
I don't know where is the problem but the controller showing
description : The requested resource is not available.
Here is my code :
POM.XML
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lucia</groupId>
<artifactId>springtesting</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>spring-testing</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>5.0.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
<build>
<finalName>springtesting</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <!-- or whatever current version -->
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
AppInitializer.java
package com.lucia.config;
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[] { WebMvcConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
WebMvcConfig.java
package com.lucia.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.lucia")
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp().prefix("/WEB-INF/views/").suffix(".jsp");
}
}
UserController.java
package com.lucia.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.lucia.model.User;
#Controller
public class UserController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView hello(HttpServletResponse response) throws IOException {
ModelAndView mv = new ModelAndView();
User user = new User();
user.setUserName("Test User name");
mv.addObject("name", user.getUserName());
mv.setViewName("home");
return mv;
}
}
User.java
package com.lucia.model;
public class User {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
And Finally this is my home.jsp
home.jsp
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>Hello : ${name}
</body>
</html>
Someone help me i'm new to java and i'm stuck at this configurations.
This is my project Structure
This is the error that shown as a output
when I tried with your configuration, I had to add below dependency
then it worked fine
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
Is it possible to pass object let's say User (who contains 3 String attributes - name, password, detail) to thymeleaf template via context myContext.setVariable("user", myUser) and access it attributes from template like this <div th:text="${user.name}"/> ?
If so how can I do that ?
My object contains a lot of attributes and I am trying to avoid creating context with a lot of variables.
I am very new to thymeleaf so thank you for any help.
If you are using spring and thymeleaf then they should work for you like a charm. In the case it's as simple as:
private static final VAR_USER = "user"
#Autowired
private SpringTemplateEngine templateEngine;
...
public void method(User user,...) {
Map<String, Object> variables;
variables.put(VAR_USER, user);
context.setVariables(variables);
org.thymeleaf.context.Context context = new Context(locale);
String evaluated = templateEngine.process("myTemplate", context);
}
where myTemplate refers to resources/mails/myTemplate.html and it's content looks like:
<p th:text="#{email.userActivity.greeting}">Hello</p>
<p th:text="#{email.userActivity.text1}">Following user activity...</p>
<ul>
...
<li th:text="#{email.userActivity.phone(${user.phoneNumber}?: #{error.not.provided})}">Phone number:</li>
<li th:text="#{email.userActivity.membershipNumber(${user.membershipNumber}?: #{error.not.provided})}">Membership number:</li>
...
</ul>
<p th:text="#{email.userActivity.text2}">Thanks for taking care of this demand within the agreed period!</p>
<p th:text="#{email.userActivity.text3}">Regards</p>
and my User entity
public class User implements Serializable {
...
#Column(name = "membership_number")
private String membershipNumber;
#Column(name = "phone_number")
private String phoneNumber;
...
}
Then, my Thymeleaf configuration:
package my.package.config;
import my.package.MyTemplateEngine;
import org.apache.commons.lang3.CharEncoding;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.StringTemplateResolver;
#Configuration
public class ThymeleafConfiguration {
private MyTemplateEngine templateEngine;
#Bean
#Description("Thymeleaf template resolver serving HTML 5 emails from template file")
public ITemplateResolver htmlTemplateResolver() {
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("mails/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode(TemplateMode.HTML);
emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
emailTemplateResolver.setCheckExistence(true);
return emailTemplateResolver;
}
#Description("Thymeleaf template resolver serving HTML 5 emails from input string")
#Bean
public ITemplateResolver stringTemplateResolver() {
final StringTemplateResolver templateResolver = new StringTemplateResolver();
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
#Bean
public MyTemplateEngine createTemplateEngine() {
templateEngine = new MyTemplateEngine();
return templateEngine;
}
}
and the version of Thymeleaf that I use:
<properties>
...
<thymeleaf-layout-dialect.version>2.2.1</thymeleaf-layout-dialect.version>
<thymeleaf.version>3.0.6.RELEASE</thymeleaf.version>
...
</properties>
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
...
</dependencies>
I am trying to implement AOP in spring-boot with the help of custom annotation and Around advice. I am adding the annotation before a GET api but i am not able to catch the function in my Aspect.
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>compile</scope>
<version>1.8.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
HiveService.java
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
public #interface HiveService{
//public String name() default "";
}
Followup.java
#HiveService
#RequestMapping(value="rest/services/{pr
oduct}", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public String getFollowupCategoryList(#PathVariable String product) {
LOGGER.info("[getFollowupCategoryList]: started again ");
}
HiveConsumer.java
#Aspect
#Component
public class HiveConsumer {
#Around("#annotation(com.abc.xyz.HiveService)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Logger LOGGER = LoggerFactory.getLogger(com.abc.xyz.FollowupController.class);
LOGGER.info("Before");
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
LOGGER.info(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
Configuration file for Spring AOP
#SpringBootApplication
#RestController
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class ActivitiRestApplication extends
SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(applicationClass);
}
private static Class<ActivitiRestApplication> applicationClass = ActivitiRestApplication.class;
The application runs nicely if you
add dependency org.springframework.boot:spring-boot-starter-web:2.0.3.RELEASE to your POM,
remove the redundant org.aspectj:aspectjrt because it is a subset of org.aspectj:aspectjweaver (and you had specified a non-matching version umber anyway),
add #RestController to your class containing the annotated method,
make method getFollowupCategoryList(..) actually return a string (in your sample code it does not return anything, thus does not even compile).
Here is the fixed version of your code as a full MCVE, i.e. including package and class names. I changed the logging code to use System.out so as to minimise dependencies for the sake of this demo.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow</groupId>
<artifactId>spring-boot-51512380</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>compile</scope>
<version>1.8.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
Update: Actually, you can even reduce your POM to this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow</groupId>
<artifactId>spring-boot-51512380</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
public #interface HiveService {
//public String name() default "";
}
package de.scrum_master.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SomeClass {
#HiveService
#RequestMapping(value = "rest/services/{product}", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public String getFollowupCategoryList(#PathVariable String product) {
System.out.println("[getFollowupCategoryList]: started again");
return "foo";
}
}
package de.scrum_master.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class ActivitiRestApplication extends SpringBootServletInitializer {
private static Class<ActivitiRestApplication> applicationClass = ActivitiRestApplication.class;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(applicationClass);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(applicationClass, args);
}
}
package de.scrum_master.app;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class HiveConsumer {
#Around("#annotation(de.scrum_master.app.HiveService)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("Before");
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
Now if you run the application and open URL http://localhost:8080/rest/services/something in a web browser, it will display foo in the browser and this on the console:
Before
[getFollowupCategoryList]: started again
String de.scrum_master.app.SomeClass.getFollowupCategoryList(String) executed in 2ms
Exception is:
An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'machineController': Unsatisfied dependency expressed through field 'machineRepository': Error creating bean with name 'machineRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property machine found for type Machine!; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'machineRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property machine found for type Machine!
Editor for coding, is Subline, Code for the project
main
Main class for springboot application
package com.brommarest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
importorg.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EntityScan(basePackages = {"com.brommarest.entities" })
#EnableJpaRepositories(basePackages = {"com.brommarest.repositories"})
public class BrommaRestApplication {
public static void main(String[] args) {
SpringApplication.run(BrommaRestApplication.class, args);
}
}
Repositories repositoty file, MachineRepository.java
Repository for the Machine entity
package com.brommarest.repositories;
import com.brommarest.entities.Machine;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.Set;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
#Repository
public interface MachineRepository extends JpaRepository<Machine, Integer> {
// custom query to search to machine by type or description
List<Machine> findBymachine_typeOrmachine_desc(String text, String
textAgain);
}
entities entity filem, Machine.java
entity class for the repository
package com.brommarest.entities;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Machine {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int machine_id;
private String machine_type;
private String machine_desc;
private String date_added;
public Machine() { }
public Machine(String machine_type, String machine_desc, String date_added)
{
this.setTitle(machine_type);
this.setDesc(machine_desc);
this.setDate(date_added);
}
public Machine(int machine_id, String machine_type, String machine_desc,
String date_added) {
this.setId(machine_id);
this.setTitle(machine_type);
this.setDesc(machine_desc);
this.setDate(date_added);
}
public int getId() {
return machine_id;
}
public void setId(int machine_id) {
this.machine_id = machine_id;
}
public String getTitle() {
return machine_type;
}
public void setTitle(String machine_type) {
this.machine_type = machine_type;
}
public String getDesc() {
return machine_desc;
}
public void setDesc(String machine_desc) {
this.machine_desc = machine_desc;
}
public String getDate() {
return date_added;
}
public void setDate(String date_added) {
this.date_added = date_added;
}
#Override
public String toString() {
return "Machine{" +
"machine_id=" + machine_id +
", machine_type='" + machine_type + '\'' +
", machine_desc='" + machine_desc + '\'' +
", date_added='" + date_added + '\'' +
'}';
}
}
Controller MachineController.java
Controller for the project
package com.brommarest.controllers;
import com.brommarest.entities.Machine;
import com.brommarest.repositories.MachineRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
#RestController
public class MachineController {
#Autowired
MachineRepository machineRepository;
#GetMapping("/machine")
public List<Machine> index(){
return machineRepository.findAll();
}
#GetMapping("/machine/{machine_id}")
public Machine show(#PathVariable String machine_id){
int machineId = Integer.parseInt(machine_id);
return machineRepository.findOne(machineId);
}
#PostMapping("/machine/search")
public List<Machine> search(#RequestBody Map<String, String> body){
String searchTerm = body.get("text");
return machineRepository.findBymachine_typegOrmachine_desc(searchTerm,
searchTerm);
}
#PostMapping("/machine")
public Machine create(#RequestBody Map<String, String> body){
String machine_type = body.get("machine_type");
String machine_desc = body.get("machine_desc");
String date_added = body.get("date_added");
return machineRepository.save(new Machine(machine_type, machine_desc,
date_added));
}
#PutMapping("/machine/{machine_id}")
public Machine update(#PathVariable String machine_id, #RequestBody
Map<String, String> body){
int machineId = Integer.parseInt(machine_id);
// getting machine
//Machine machine = MachineRepository.findOne(machineId);
Machine machine = machineRepository.findOne(machineId);
machine.setTitle(body.get("machine_type"));
machine.setDesc(body.get("machine_desc"));
return machineRepository.save(machine);
}
#DeleteMapping("/machine/{machine_id}")
public boolean delete(#PathVariable String machine_id){
int machineId = Integer.parseInt(machine_id);
machineRepository.delete(machineId);
return true;
}
}
application.properties
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.jpa.show-sql=true
spring.database.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=default
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
pom file
<groupId>com.brommarest</groupId>
<artifactId>brommarest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Bromma-REST</name>
<description>Bromma-REST API'S project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-social-twitter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-
beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Interface signature should follow name convention. Spring generates queries based on the method signature and it cannot.
I guess you should capitalize property names. Please try this:
List<Machine> findByMachine_typeOrMachine_desc(String text, String
textAgain);
No underscore ("_") business is Java, unless you know what exactly you are doing. Rename all the methods and field names to use camelCase. Spring is expecting things to be in a certain way, otherwise it will misbehave.
#GetMapping("/machine/{machine_id}") this "_" is fine
But this public Machine show(#PathVariable String machine_id){ is not
I have an XML file that I am trying to read that has elements with attributes. I have tried from multiple examples, but the fields in my class always end up as null as shown below:
Data [type=null, value=null]
Data [type=null, value=null]
Data [type=null, value=null]
Below is the cut down example code of my issue.
Here is an example XML file located in the src/main/resources/data directory (data.xml):
<?xml version="1.0" encoding="UTF-8"?>
<list>
<data type="shopping" value="milk" />
<data type="shopping" value="eggs" />
<data type="TODO" value="return books to library" />
</list>
Below is my domain class for the XML data (Data.java):
package com.example.demo.springbatchtest.domain;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "data")
public class Data {
private final String type;
private final String value;
public Data(String type, String value) {
this.type = type;
this.value = value;
}
#XmlAttribute(name = "type")
public String getType() {
return type;
}
#XmlAttribute(name = "value")
public String getValue() {
return value;
}
#Override
public String toString() {
return "Data [type=" + type + ", value=" + value + "]";
}
}
Here is my Spring Batch job configuration file (JobConfiguration.java):
package com.example.demo.springbatchtest.configuration;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.xml.StaxEventItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.xstream.XStreamMarshaller;
import com.example.demo.springbatchtest.domain.Data;
#Configuration
public class JobConfiguration {
#Autowired
public JobBuilderFactory jobBuilderFactory;
#Autowired
StepBuilderFactory stepBuilderFactory;
#Bean
public StaxEventItemReader<Data> dataItemReader() {
XStreamMarshaller unmarshaller = new XStreamMarshaller();
Map<String, Class> aliases = new HashMap<>();
aliases.put("data", Data.class);
unmarshaller.setAliases(aliases);
StaxEventItemReader<Data> reader = new StaxEventItemReader<>();
reader.setResource(new ClassPathResource("/data/data.xml"));
reader.setFragmentRootElementName("data");
reader.setUnmarshaller(unmarshaller);
return reader;
}
#Bean
public ItemWriter<Data> dataItemWriter() {
return items -> {
for (Data item : items) {
System.out.println(item.toString());
}
};
}
#Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.<Data, Data>chunk(10)
.reader(dataItemReader())
.writer(dataItemWriter())
.build();
}
#Bean
public Job job() {
return jobBuilderFactory
.get("job")
.start(step1())
.build();
}
}
Here is my main Spring Boot class (SpringBatchTestApplication.java):
package com.example.demo.springbatchtest;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
#EnableBatchProcessing
public class SpringBatchTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchTestApplication.class, args);
}
}
Here is my pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>SpringBatchTestSpringBoot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBatchTestSpringBoot</name>
<description>Spring Batch Test with Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
It sounds like JAX-B is unable to set the Data.type and value attributes.
By default it will be looking for a getter/setter pair for each attribute, otherwise they will be treated as read-only.
The alternative would be to use field level access - #XmlAccessorType(XmlAccessType.FIELD)
Found this tutorial which helped me get the example code to work.
https://walkingtechie.blogspot.com/2017/03/spring-batch-xml-file-to-mysql-example.html
The key was to a Converter class to parse out the attributes. So in my example, I added a call set a Converter class in the dataItemReader() in the JobConfiguration class.
import com.example.demo.springbatchtest.converter.DataConverter;
...
#Autowired
private DataConverter dataConverter;
...
#Bean
public StaxEventItemReader<Data> dataItemReader() {
XStreamMarshaller unmarshaller = new XStreamMarshaller();
Map<String, Class> aliases = new HashMap<>();
aliases.put("data", Data.class);
unmarshaller.setAliases(aliases);
unmarshaller.setConverters(dataConverter); // from Walking Techie
StaxEventItemReader<Data> reader = new StaxEventItemReader<>();
reader.setResource(new ClassPathResource("/data/data.xml"));
reader.setFragmentRootElementName("data");
reader.setUnmarshaller(unmarshaller);
return reader;
}
And then I extended the Converter class to handle the attributes.
package com.example.demo.springbatchtest.converter;
import org.springframework.stereotype.Component;
import com.example.demo.springbatchtest.domain.Data;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
#Component
public class DataConverter implements Converter {
#Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
}
#Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
String type = reader.getAttribute("type");
String value = reader.getAttribute("value");
return new Data(type, value);
}
#Override
public boolean canConvert(Class type) {
return type.equals(Data.class);
}
}
The program now outputs the following:
Data [type=shopping, value=milk]
Data [type=shopping, value=eggs]
Data [type=TODO, value=return books to library]