I have a Spring-Boot API with the endpoint below. It is throwing a Null Pointer Exception on a Spring Data JPA findAll query; when I comment out this line, I get no errors. It seems that I am getting a null result from the repository query, but I know the data is there from querying the DB directly. I cannot understand why I'm getting a null for topicsLookup variable... Can anyone point me in the right direction?
Resource:
#RequestMapping(value = "/lectures/{lectureId}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, SpeakerTopicLectures> getLecture(#PathVariable Long lectureId){
Long requestReceived = new Date().getTime();
Map<String, SpeakerTopicLectures> result = new HashMap<>();
log.debug("** GET Request to getLecture");
log.debug("Querying results");
List<SpeakerTopicLectures> dataRows = speakerTopicLecturesRepository.findBySpeakerTopicLecturesPk_LectureId(lectureId);
// This line throws the error
List<SpeakerTopic> topicsLookup = speakerTopicsRepository.findAll();
// Do stuff here...
log.debug("Got {} rows", dataRows.size());
log.debug("Request took {}ms **", (new Date().getTime() - requestReceived));
// wrap lecture in map object
result.put("content", dataRows.get(0));
return result;
}
Java Bean:
#Entity
#Table(name = "speaker_topics")
#JsonInclude(JsonInclude.Include.NON_NULL)
#Data
public class SpeakerTopic implements Serializable {
#Id
#Column(name = "topic_id")
private Long topicId;
#Column(name = "topic_nm")
private String topicName;
#Column(name = "topic_desc")
private String topicDesc;
#Column(name = "topic_acm_relt_rsce")
private String relatedResources;
}
Repository:
import org.acm.dl.api.domain.SpeakerTopic;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpeakerTopicsRepository extends JpaRepository<SpeakerTopic,Long> {
}
The most likely cause is that speakerTopicsRepository is itself null, which is probably caused by forgetting to autowire it, e.g.
public class YourController {
#Autowired private SpeakerTopicsRepository speakerTopicsRepository;
#RequestMapping(value = "/lectures/{lectureId}",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, SpeakerTopicLectures> getLecture(#PathVariable Long lectureId) {
// your method...
}
}
the repository is not autowired in your controller.
Try using
#Repository
#Transactional
public interface SpeakerTopicsRepository extends JpaRepository<SpeakerTopic,Long> {
// Your Repository Code
}
I think #Repository & #Transactional is missing. Please use it.
It was missing #Autowired for me.
Related
I am trying to a write a spring boot controller which can return the result of a native query as json. I will be passing the query as input parameter and the return must be result of the query. Is there a way to do this? I know the http rpc help on this. The query can be anything and the system must accept it and must respond with the result as json.
For example if I pass the request as select * from employee it must respond with result of query as json.
Simply make every function returning:
Map<String, Object>
It will automatically map the object property and value. That means a json object is an instance of Map. If you are managing an array of it, enclose it with a List:
List<Map<String, Object>>
and finally the ResponseEntity becomes:
ResponseEntity<List<Map<String, Object>>>
You could actually use Spring JDBC for that,
Repo
#Repository
public class FooRepo {
#Autowire
private JdbcTemplate jdbcTemplate;
public Object returnDataForQuery(String sql) {
return jdbcTemplate.queryForObject(sql, Object.class); // You could define a proper class if you know the return Type else returning plain object is more then enough
// return jdbcTemplate.queryForList(sql, Object.class) Incase Multiple Data
}
}
Model
public class FooDto {
private String query;
// Getter, Setter & No Args Constructor (or) Lombok
}
Controller
#Autowire
private FooRepo fooRepo;
#PostMapping(value = "/postData", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity postData(#RequestBody FooDto foo) {
return ResponseEntity.ok(fooRepo.returnDataForQuery(foo.getQuery);
}
This is just a overview, you could bend it.As for your result output concern you ResponseEntity will take care of it
SpringBoot
//Controller Class
#RestController
#RequestMapping("/employee")
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#GetMapping("/all")
public List<Employee> getAllEmplpyee() {
logger.info("get All Employeee");
return employeeService.getAllEmployeeService();
}
}
//ServiceImpl
#Service
public class EmployeeService {
private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class);
#Autowired
private EmployeeRepository employeeRepository;
public List<Employee> getAllEmployeeService() {
logger.info(getClass().getName()," invked getAllEmployee");
List<Employee> empBo = employeeRepository.findAll();
return copyPropertiesValues(empBo);
}
}
//DAO
#Component
public interface EmployeeRepository extends JpaRepository<Employee, String>{
}
//Model
#Entity
#Table(name = "employees")
public class Employee {
#Id
#Column(name = "employeeNumber",nullable=false)
private String employeeNumber;
#Column(nullable=false)
private String lastName;
#Column(nullable=false)
private String firstName;
#Column(nullable=false)
private String extension;
#Column(nullable=false)
private String email;
#Column( nullable=false)
private String officeCode;
#Column(nullable=false)
private String reportsTo;
#Column(nullable=false)
private String jobTitle;
//GETTER SETTER
}
//application.properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=****
User model:
#Entity
#Table(name="user")
public class User {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#NotBlank
#Column(name="username")
private String username;
#NotEmpty
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="user_role", joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="role_id")})
private Set<Role> roles;
}
Controller:
#RequestMapping(value = {"/users/edit/{id}"}, method = RequestMethod.POST)
public String editUser(ModelMap model, #Valid #ModelAttribute("user") User user, BindingResult result) {
if(result.hasErrors()) {
return "AddUserView";
}
return "redirect:/users";
}
Test with MockMVC:
#Test
public void performUpdateUserTest() throws Throwable {
mockMvc.perform(post("/users/edit/{id}", user.getId())
.param("username", "User"));
}
Well, fine, I can pass a param username as always using param(). But what should I do with ROLES? This field is a separate object. I can't pass it using param(). Then how is it possible to pass it in the test?
The only way out I found is to create an entity and pass it using .flashAttr():
#Test
public void performUpdateUserTest() throws Throwable {
User user = new User("User", new HashSet<Role>(Arrays.asList(new Role("USER"))));
mockMvc.perform(post("/users/edit/{id}", user.getId())
.flashAttr("user", user));
}
But then, what if I need to test that user can't be updated because of binding error in the ROLES field(ROLES can't be null, and suppose, it was set as null)? Thus, I'm not able to create user(and use it with .flashAttr) already with a binding error as the exception will be thrown. And I still have to pass it separately.
Well, after a long time of searching, I found out that I should add a converter to the MockMVC. What converter is you can read HERE, for instance.
I had it already in my project but didn't realize that it didn't work with MockMVC.
So, you can add the converter to MockMVC like that:
#Autowired
private StringToRoleConverter stringToRoleConverter;
#Before
public void init() {
FormattingConversionService cs = new FormattingConversionService();
cs.addConverter(stringToRoleConverter);
mockMvc = MockMvcBuilders.standaloneSetup(userController)
.setConversionService(cs)
.build();
}
Converter itself:
#Component
public class StringToRoleConverter implements Converter<String, Role> {
#Autowired
private RoleService roleService;
#Override
public Role convert(String id) {
Role role = roleService.findById(Integer.valueOf(id));
return role;
}
}
And then I can add param like that:
mockMvc.perform(post("/users/edit/{id}", user.getId())
.param("roles", "2"))
though I'm passing a string there, it will be converter to Role with the help of Spring converter.
I have the following domain object and DTO defined.
Country.java
#Data
#Entity
public class Country extends ResourceSupport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long countryID;
#NotBlank(message = "Country name is a required field")
private String countryName;
private String countryNationality;
}
CountryDTO.java
#Data
public class CountryDTO {
private List<Country> countries;
}
I have overridden the POST method in the RepositoryRestController for the country class.
#RepositoryRestController
public class CountryController {
#Autowired
private CountryRepository repo;
#RequestMapping(method = POST, value = "countries")
public #ResponseBody ResponseEntity<?> createCountry(#RequestBody Resource<CountryDTO> dto,
Pageable page, PersistentEntityResourceAssembler resourceAssembler) {
Country savedCountry = repo.save(dto.getContent().getCountries());
return new ResponseEntity<>(resourceAssembler.toResource(savedCountry), HttpStatus.OK);
}
}
Now I have defined a RepositoryEventHandler to handle validations.
#Component
#RepositoryEventHandler
public class CountryHandler {
#HandleBeforeCreate
public void handleBeforeCreate(Country country) {
System.out.println("testing");
}
But when I send a POST request to the endpoint http://localhost:8080/countries, the eventhandler does not get invoked. Is there anything I am doing wrong?
UPDATE 1:
I am sending the following JSON to the endpoint using Postman.
"countries":[{
"countryName":"Australia",
"countryNationality":"Australian"
}]
It is difficult to give you an exact solution not knowing how you are invoking the request. But possible reason is that you are missing the slash symbol #RequestMapping value attribute:
#RequestMapping(method = POST, value = "countries")
Should be:
#RequestMapping(method = POST, value = "/countries")
Define a Bean in AppConfigration as
#Configuration
#EnableAsync
public class AppConfig {
#Bean
CountryHandler countryHandler (){
return new CountryHandler ();
}
}
It will work then.
Try editing maybe the Controller class annotation from:
#RepositoryRestController
to
#RestController
and mainly the method annotation from:
#RequestMapping(method = POST, value = "countries")
to
#RequestMapping(value = "/countries", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
PS: produces = MediaType.APPLICATION_JSON_VALUE if you are going to return json.
I know this is older but this works as it is supposed to.
The methods defined in a #RepositoryRestController implementation replace the methods in the default RepositoryEntityController which publish #RepositoryEventHandler events.
So your controller needs to publish a create event:
#RepositoryRestController
public class CountryController {
#Autowired
private CountryRepository repo;
private final ApplicationEventPublisher publisher; //This changed
#RequestMapping(method = POST, value = "countries")
public #ResponseBody ResponseEntity<?> createCountry(#RequestBody Resource<CountryDTO> dto,
Pageable page, PersistentEntityResourceAssembler resourceAssembler) {
Country savedCountry = repo.save(dto.getContent().getCountries());
publisher.publishEvent(new BeforeCreateEvent(savedCountry)); //This changed
return new ResponseEntity<>(resourceAssembler.toResource(savedCountry), HttpStatus.OK);
}
}
I'm a newbie coder having just finished a 6 month coding crash-course. I'm working on a java webapp to demonstrate my skills, and the project idea I had involves retrieving JSON data from an API, something we didn't learn about in class. I made POJOs to match the JSON, and I'm trying to parse the JSON into java objects to store in a database, however my database tables are never filled with data when I run through the app. I suspect the problem is somewhere with my method to convert the JSON but any feedback is greatly appreciated. Here's all my code I think is relevant, sorry if its TMI. I also apologize if my code is ugly, I'm a beginner... Thanks!
API returns JSON like this:
{
"result":{
"status":1,
"num_results":1,
"total_results":500,
"results_remaining":499,
"matches":[{
"match_id":3188095188,
"match_seq_num":2784956606,
"start_time":1495079320,
"lobby_type":7,
"radiant_team_id":0,
"dire_team_id":0,
"players":[{
"account_id":86920222,
"player_slot":0,
"hero_id":18
},{
"account_id":61122568,
"player_slot":1,
"hero_id":85
},{
"account_id":10208661,
"player_slot":2,
"hero_id":13
},{
"account_id":106083675,
"player_slot":132,
"hero_id":50
}]
}]
}
}
My POJOs:
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Result {
#JsonIgnore
#Id
#GeneratedValue
private int id;
#JsonProperty("status")
private int status;
#JsonProperty("num_results")
private int num_results;
#JsonProperty("total_results")
private int total_results;
#JsonProperty("results_remaining")
private int results_remaining;
#OneToMany
#JoinColumn(name = "result_id")
#ElementCollection(targetClass=Matches.class)
#JsonProperty("matches")
private List<Matches> matches;
// getters and setters
}
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Matches {
#Id
#JsonProperty("match_id")
private int match_id;
#JsonIgnore
#ManyToOne
private Result result;
#JsonProperty("match_seq_num")
private int match_seq_num;
#JsonProperty("start_time")
private int start_time;
#JsonProperty("lobby_type")
private int lobby_type;
#JsonProperty("radiant_team_id")
private int radiant_team_id;
#JsonProperty("dire_team_id")
private int dire_team_id;
#OneToMany
#JoinColumn(name = "Matches_id")
#ElementCollection(targetClass=Players.class)
#JsonProperty("players")
private List<Players> players;
// getters and setters
}
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Players {
#JsonIgnore
#Id
#GeneratedValue
private int id;
#JsonIgnore
#ManyToOne
private Matches matches;
#JsonProperty("account_id")
private int account_id;
#JsonProperty("player_slot")
private int player_slot;
#JsonProperty("hero_id")
private int hero_id;
// getters and setters
}
Services method to read and convert the JSON to objects (url is censored, don't want my API key to be public)
public class SteamService {
public static Result getMatchHistory(String steamid){
Result result = new Result();
String MatchHistoryUrl = "https:**URL**="+steamid;
RestTemplate restTemplate = new RestTemplate();
Result jsonresult = restTemplate.getForObject(MatchHistoryUrl, Result.class);
return jsonresult;
}
}
Controller
#Controller
#RequestMapping("")
public class HomeController {
#Autowired
private ResultsDao resultsDao;
#RequestMapping(value = "", method = RequestMethod.GET)
public String index(Model model){
model.addAttribute("title", "Welcome");
return "home/home";
}
#RequestMapping(value = "", method = RequestMethod.POST)
public String processSteamIdField(#RequestParam("steamid")String steamid, Model model) {
Result newresult = getMatchHistory(steamid);
resultsDao.save(newresult);
return "redirect:results";
}
}
DAO
#Repository
#Transactional
public interface ResultsDao extends CrudRepository<Result, Integer>{
}
Maybe my approach is a bit naive, but... If you want to store the JSON as string in the database, then I would use an object mapper for this:
new ObjectMapper().writeValueAsString(myObject);
and for reading a JSON and parsing it to a class I would do:
new ObjectMapper().readValue(JSON_STRING_HERE, "utf-8"), MyPOJO.class);
Also, if you already are using Spring, then your controller may look like this (for a POST, for example)
#RequestMapping(value = "/", method = RequestMethod.POST)
public MyPojo myController(#RequestBody MyPojo myBody) {
myRepository.save(myBody);
}
So, the parsing of the JSON that the client is sending to your app and your controller is already handled by Spring
I'm attempting to call a MS SQL server stored procedure. Im using spring-boot, JPA 2.1, hibernate.
The database has a table with isbn, title, author, description and the stored procedure i'm trying to call takes one in parameter(isbn) as a string and returns only the title.
I get the following error:
org.hibernate.procedure.ParameterStrategyException:
Attempt to access positional parameter [2] but ProcedureCall using named parameters
Anyone got a solution for this or know what the error means? I have also tried other combinations of annotations.
Book.java
#Entity
#NamedStoredProcedureQuery(
name = "bookList",
resultClasses=Book.class,
procedureName = "dbo.list_books",
parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "isbn", type = String.class)
})
public class Book {
#Id
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
BookRepository.java
#Repository
public interface BookRepository extends CrudRepository<Book, Long> {
#Procedure
Iterable<Book> list_books(String arg);
}
BookService.java
#RestController
#RequestMapping(value = "/books", produces = MediaType.APPLICATION_JSON_VALUE)
public class BookService {
#Autowired
protected BookRepository bookRepository;
#RequestMapping
public Iterable<Book> books(){
return bookRepository.getBooks("1111111");
}
I didn't solve the problem with annotations, i worked around it with an EntityManager and a StoredProcedureQuery.
The Book.java is the same but without the #NamedStoredProcedureQuery. I removed the repository and rewrote the service like this:
#RestController
#RequestMapping("/api")
public class BookService {
#RequestMapping(value = "/books",
params = {"isbn"},
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Book> getByIsbn(#RequestParam(value = "isbn") String isbn){
StoredProcedureQuery sp = em.createStoredProcedureQuery("name.of.stored.procedure", Book.class);
sp.registerStoredProcedureParameter("isbn", String.class, ParameterMode.IN);
sp.setParameter("isbn", isbn);
boolean result = sp.execute();
if (result == true) {
return sp.getResultList();
} else {
// Handle the false for no result set returned, e.g.
throw new RuntimeException("No result set(s) returned from the stored procedure");
}
}
}
It is now possible to call this endpoint with a stringquery like: http://localhost/api/books?isbn=1111111