MyBatis #Many / Spring-Boot - spring-boot

I'm beginner (sorry for my bad explanation, feel free to correct me) in MyBatis Spring-Boot, I have problem to understand and make it works #Many
I'm using 3 layer logic programming (Presentation Layer, Service Layer, Data Layer)
Thanks for your help :)
I have 3 Tables (it's TB_Products and not TB_Product as on the screenshot):
I would like to get data form table TB_Users and TB_Products to "put" it in DTO
I create 4 java object class SearchEntity, ProductEntity (for Data layer)
I create an interface SearchRepositoryMapper.
I also create a SearchService interface and SearchServiceImpl as well.
Java object class:
SearchEntity
public class SearchEntity implements Serializable{
private static final long serialVersionUID = -9143930742617602050L;
private String id;
private String firstName;
private String lastName;
private List<ProductEntity> products;
// Getters and Setters code .....
}
ProductEntity
public class ProductEntity implements Serializable{
private static final long serialVersionUID = -6525703679290992635L;
private String id;
private String productId;
private String product;
private String number;
private String date;
private String description;
// Getters and Setters code .....
}
SearchRepositoryMapper
public interface SearchRepositoryMapper {
// Get some fields from TB_Users and all fields from TB_Products
#Select("SELECT * FROM TB_Users WHERE id = #{id}")
#Results({
#Result(property = "id", column ="id"),
#Result(property = "firstName", column = "firstName"),
#Result(property = "lastName", column= "lastName"),
#Result(property = "products", javaType = List.class, column="id",
many = #Many(select = "getProductIdByUserId"))})
public SearchEntity findAllInfoByUserId(#Param("id") int id);
#Select("SELECT *, productId FROM TB_Products WHERE productId = #{id}")
public ArrayList<ProductEntity> getProductIdByUserId(#Param("id") int id);
// Find id by uderId and return null if it doesn't exist
#Select("SELECT id FROM TB_Users WHERE userId = #{userId}")
int findIdByUserId(#Param("userId") String userId);
}
SearchServiceImpl
#Service
public class SearchServiceImpl implements SearchService {
#Autowired
SearchRepositoryMapper searchRepository;
#Override
public SearchDto getAllInfoByUserId(String id) {
SearchDto returnValue = new SearchDto(); // Init returnValue as SearchDto
int searchId = searchRepository.findIdByUserId(id); // Init searchId with the TB_Users id
SearchEntity searchEntity = searchRepository.findAllInfoByUserId(searchId);
BeanUtils.copyProperties(searchEntity, returnValue);
return returnValue;
}
}
So when I execute the code and do a GET request I get this error message:
{
"message": "nested exception is org.apache.ibatis.executor.ExecutorException: Statement returned more than one row, where no more than one was expected."
}
I found out that come from the mapper and SearchEntity searchEntity = searchRepository.findAllInfoByUserId(searchId);
But i don't know how to resolve it. The way I wrote the code is wrong
Thanks to correct me

The exception clearly says that the query returns multiple results. Plese verify if the data in the table is correct.

Related

What causes unability to fetch entities in this code?

So i'm developing a REST API for my Spring appplication. I have to store all data in H2 database and i'm trying to find a correct way to do so. I'm new to JPA and databases and general and need help understanding the causes of errors here.
First, i have these entities.
Position.java:
package com.example.offerserver.offerservice.task1;
#Entity
#Table(name = "position_table")
public class Position {
public Position() {
}
public Position(UUID id, String name, Integer salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
#Id
private UUID id;
#Column(name = "name")
private String name;
#Column(name = "salary")
private Integer salary;
//getters and setters
Stuff.java:
package com.example.offerserver.offerservice.task1;
#Entity
#Table(name = "stuff_table")
public class Stuff {
public Stuff(){};
public Stuff(UUID id,
String surname,
String name,
String patronymic,
boolean sex,
LocalDate birthDate,
Double salaryMultiplier,
Position position) {
this.id = id;
this.surname = surname;
this.name = name;
this.patronymic = patronymic;
this.sex = sex;
this.birthDate = birthDate;
this.salaryMultiplier = salaryMultiplier;
this.position = position;
}
#Id
private UUID id;
#Column(name="surname")
private String surname;
#Column(name="name")
private String name;
#Column(name="patronymic")
private String patronymic;
#Column(name="sex")
private boolean sex;
#Column(name="birth_date")
private LocalDate birthDate;
#Column(name="salary_multiplier")
private Double salaryMultiplier;
#OneToOne(fetch = FetchType.LAZY)
private Position position;
And JPA repositories:
package com.example.offerserver.repository;
#Repository
public interface StuffRepository extends JpaRepository<Stuff, String> {
}
package com.example.offerserver.repository;
#Repository
public interface PositionRepository extends JpaRepository<Position, UUID> {
}
And i have this request:
package com.example.offerserver.controller;
#Controller
#RequestMapping("/api/v1/stuff")
public class StuffListController {
#Autowired
StuffRepository repository;
#GetMapping("")
public ResponseEntity<List<Stuff>> getStuffList(){
List<Stuff> stuff = repository.findAll();
return new ResponseEntity<>(stuff, HttpStatus.OK);
Sending this request i'm getting this error:
Could not write JSON: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59 (through reference chain: java.util.ArrayList[0]->com.example.offerserver.offerservice.task1.Stuff["position"]->com.example.offerserver.offerservice.task1.Position$HibernateProxy$E63ZeIxs["id"])
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to find com.example.offerserver.offerservice.task1.Position with id f0bd15f2-74c1-4979-854b-ffbe44b3da59 (through reference chain: java.util.ArrayList[0]->com.example.offerserver.offerservice.task1.Stuff["position"]->com.example.offerserver.offerservice.task1.Position$HibernateProxy$E63ZeIxs["id"])
In debug every instance of stuff in the list is initialized without its "position" field, throwing an error:
Method threw 'javax.persistence.EntityNotFoundException' exception. Cannot evaluate com.example.offerserver.offerservice.task1.Position$HibernateProxy$2ZiRYZbP.toString()
This is how position repository is initialized on launch:
public static List<Position> POSITIONS = List.of(
new Position(UUID.randomUUID(), "Junior Java Backend Developer", 60000),
new Position(UUID.randomUUID(), "Middle Machine Learning Engineer", 120000),
new Position(UUID.randomUUID(), "Senior DevOps Engineer", 200000),
new Position(UUID.randomUUID(), "Senior DevOps Engineer", 150000),
new Position(UUID.randomUUID(), "Intern System Engineer", 20000)
);
positionRepository.saveAll(POSITIONS);
Stuff repository as well. Position field for every stuff instance is randomly chosen from a POSITIONS list.

Insert and update data automatically into SQL table from another table

I have 2 tables and I want to update the first table with data after that the second table will be updated automatically.
I'm a beginner in spring boot and I really need your help.
I could insert data from table 1 to table 2 but if I update some data from table 1 then table 2 coudn't be updated.
What can I do?
This is what I have done so far: The two entities of the tables and the service I worked with to insert data into table 2 from table 1.
Table 1:
#Entity
#Table(name = "formation")
public class Formation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String cursus;
private String groupeaction;
private String module;
private String formateur;
#Temporal(TemporalType.DATE)
private Date date;
private Long nbrappart;
private Long nbrabsent;
private Long hf;
private Long jf;
private Long nbrheures;
private Long tauxh;
private Long ristourneprevis;
private Long couthebergttc;
private Long coutpausecafttc;
Table 2:
#Entity
#Table(name = "tablef")
public class Tablef {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String cursus;
private Long nbrappart;
private Long Sumnbrheures;
private Long Sumjf;
private Long jhf;
private String groupeaction;
the service i used :
public Boolean InserIntoTableF(Tablef tf) {
Long id = ThreadLocalRandom.current().nextLong();
tf.setId(id);
jdbc.execute("insert into tablef (id,cursus,groupeaction
,nbrappart,sumnbrheures,sumjf,jhf)\r\n" +
"select id,cursus,groupeaction,nbrappart,sum(nbrheures),sum(jf)
,sum(jf)*nbrappart\r\n" +
" from formation \r\n" +
"group by cursus ;");
return true;
}
The controller :
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api")
#PreAuthorize("hasRole('ADMIN')")
public class FormationController {
#Autowired
private FormationService formationservice;
#Autowired
private FormationRepository formationrepository;
#GetMapping("/formations")
public List<Formation> GetAll() {
return formationrepository.findAll();
}
#GetMapping("/formations/{id}")
public ResponseEntity<Formation> getFormationById(#PathVariable(value = "id") Long formationId)
throws ResourceNotFoundException {
Formation formation = formationrepository.findById(formationId)
.orElseThrow(() -> new ResourceNotFoundException("Formation not found for this id :: " + formationId));
return ResponseEntity.ok().body(formation);
}
#PostMapping("/formations")
public Formation createFormation(#Valid #RequestBody Formation formation) {
return formationrepository.save(formation);
}
// this is how i update my entity formation (table 1)
#PutMapping("/formations/{id}")
public ResponseEntity<Formation> updateFormation(#PathVariable(value = "id") Long formationId,
#Valid #RequestBody Formation formationDetails) throws ResourceNotFoundException {
Formation formation = formationrepository.findById(formationId)
.orElseThrow(() -> new ResourceNotFoundException("Formation not found for this id :: " + formationId));
formation.setCursus(formationDetails.getCursus());
formation.setGroupeaction(formationDetails.getGroupeaction());
formation.setModule(formationDetails.getModule());
formation.setFormateur(formationDetails.getFormateur());
formation.setDate(formationDetails.getDate());
formation.setNbrappart(formationDetails.getNbrappart());
formation.setNbrabsent(formationDetails.getNbrabsent());
formation.setHf(formationDetails.getHf());
formation.setJf(formationDetails.getJf());
formation.setNbrheures(formationDetails.getNbrheures());
formation.setTauxh(formationDetails.getTauxh());
formation.setRistourneprevis(formationDetails.getRistourneprevis());
formation.setCouthebergttc(formationDetails.getCouthebergttc());
formation.setCoutpausecafttc(formationDetails.getCoutpausecafttc());
final Formation updatedFormation = formationrepository.save(formation);
return ResponseEntity.ok(updatedFormation);
}
#DeleteMapping("/formations/{id}")
public Map<String, Boolean> deleteFormation(#PathVariable(value = "id") Long formationId)
throws ResourceNotFoundException {
Formation formation = formationrepository.findById(formationId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + formationId));
formationrepository.delete(formation);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return response;
}
#PostMapping(value = "/fileupload")
public ResponseEntity<Formation> uploadFile(#ModelAttribute Formation formation) {
Boolean isFlag=formationservice.saveDataFromFile(formation.getFile());
if(isFlag) {
return new ResponseEntity<>(HttpStatus.OK);
}else
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// here where i inser data from formation(table1) to tablef (table2)
#PostMapping(value = "/test")
public Boolean AddTf(Tablef tf) {
return formationservice.InserIntoTableF(tf);
}
}
If you use spring boot jpa to persist your data then you can have a look at JPA EntityListener and #PostPersist
#Entity
#EntityListeners(MyEntityListener.class)
public class MyEntity {
#Id
#GeneratedValue
private int id;
private String field;
public MyEntity() { }
}
The MyEntityListener impl
public class MyEntityListener {
#PostPersist
void onPostPersist(MyEntity myEntity) {
// save data to second table that needs an update on myEntity save
}
}

How to code Spring JPA onetomany relation

I am new to Spring programming and trying the below example with one to many relationship between BID and BIDITEM classes. I am not sure whether the BIDITEM data is saved, as when I tried to retrieve the BID, I am getting only BID data and not BIDITEM data. Do we need to have a repository even for BIDITEM class. I can see that complete BID JSON string, along with BIDITEM is received in create method.
Could you please go through it, and let me know what is wrong with it.
#Entity
#Table(name = "bid")
public class Bid {
#Id
private String title;
#Column
private long startDate;
#Column
private long endDate;
#OneToMany(mappedBy = "myBid", cascade = {CascadeType.ALL})
private List<BidItem> bidItems = new ArrayList<BidItem>();
//Constructor, getter and setter methods go here
}
#Entity
#Table(name="biditem")
public class BidItem
{
#Id
private String item;
#Column
private String desc;
#Column
private double minAmt;
#ManyToOne
#JoinColumn(name = "title")
private Bid myBid;
//Constructor, getter and setter methods go here
}
public interface BidRepository extends CrudRepository<Bid, String> {
//Tried even JpaRepository
}
public class BidService {
ObjectMapper mapper = new ObjectMapper();
#Autowired
private BidRepository bidRepo;
public Bid create(String bidJson) throws JsonParseException, JsonMappingException, IOException
{
Bid bid = mapper.readValue(bidJson, Bid.class);
// bidJson string has below string
// {"bidItems":[{"item":"item1","desc":"item1","minAmt":"999"}],
// "title":"bid1","startDate":"D1","endDate":"D5"}
Bid savedBid = bidRepo.save(bid);
return savedBid;
}
public Bid findByID(String title)
{
Bid bid = bidRepo.findOne(title);
return bid;
}
}

NamedQuery and no entity mapping

I would like to achieve the following. I have a query and I would like to run it and return rows in a REST call.
I do not want to map the query to a physical table, how would I achieve this?
I use Spring Boot 1.5.2.
After some try and fixes, I got the following solution.
Create a POJO class, no #Entity annotation. You want to add packageScan instructions if it is not found.
public class ActivityReport1 {
#Column
private BigInteger id;
#Column
private String title;
//Only getters
public ActivityReport1(BigInteger id,
String title){
this.id = id;
this.title = title;
}
In a class which is annotated with #Entity create the resultset mapping
#SqlResultSetMappings({
#SqlResultSetMapping(name = "ActivityReport1Mapping",
classes = {
#ConstructorResult(targetClass = ActivityReport1.class, columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "title")
})
})
})
Add repository class
#Repository
#Transactional
public class IActivityReport1Repository {
#PersistenceContext
private EntityManager entityManager;
public List<ActivityReport1> getResults(String userLogin) {
Query query = entityManager.createNativeQuery(
"SELECT " +
"t.request_id as id, t.request_title as title " +
"FROM some_table t ", "ActivityReport1Mapping");
List<ActivityReport1> results = query.getResultList();
return results;
}
}
And finally, the service impl class.
#Service
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class ActivityReport1ServiceImpl implements IActivityReport1Service {
private static final Logger _Logger = LoggerFactory.getLogger(ActivityReport1ServiceImpl.class);
#Autowired
private IActivityReport1Repository sessionFactory;
#Override
public List<ActivityReport1> runReport(String userLogin) {
List<ActivityReport1> reportRows = sessionFactory.getResults(userLogin);
return reportRows;
}
}
If you face with "Could not locate appropriate constructor", this means that on Java side it could not map db types to java types.
In my case I had to change id from Long to BigInteger and Timestamp to java.util.date.

Spring Data REST and custom entity lookup (Provided id of the wrong type)

I have a model that looks something like this:
#Entity
public class MyModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(unique = true, nullable = false)
private String uuid = UUID.randomUUID().toString();
#Column(nullable = false)
private String title;
public int getPk() {
return pk;
}
public void setPk(int pk) {
this.pk = pk;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
As you can see I have an auto-incrementing PK as my ID for the model, but also a random UUID. I want to use the PK in the database as the primary key, but want to use the UUID as a public facing ID. (To be used in URLs etc.)
My repository looks like this:
#RepositoryRestResource(collectionResourceRel = "my-model", path = "my-model")
public interface MyModelRepository extends CrudRepository<MyModel, String> {
#RestResource(exported = false)
MyModel findByUuid(#Param("uuid") String id);
}
As you can see I've set the repository to use a String as the ID.
Finally I set the entity lookup in a config file like this:
#Component
public class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(MyModelRepository.class, MyModel::getUuid, MyModelRepository::findByUuid);
}
}
This works perfectly well for GET and POST requests, but for some reason I get an error returned on PUT and DELETE methods.
o.s.d.r.w.RepositoryRestExceptionHandler : Provided id of the wrong type for class MyModel. Expected: class java.lang.Integer, got class java.lang.String
Anyone know what might be causing this? I don't understand why it's expecting an Integer.
I may be doing something stupid as I'm quite new to the framework.
Thanks for any help.
The identifier of your domain object is obviously of type int. That means, your repository needs to be declared as extends CrudRepository<MyModel, Integer>.

Resources