NullPointerException problem when using mybatis - spring

CartItem
private Integer id;
private Book book;
private Integer buyCount;
private User userBean;
and in mysql db, the book and userBean is a int value which is the id in t_book and t_user
so when i want to get all cartItem by userId
List<CartItem> getCartItemList(User user);
<select id="getCartItemList" resultType="CartItem">
select * from t_cart_item where userBean = #{id}
</select>
However i get null. i think the problem is that the int result cannot be mapped as a class? But i don't know how to fix it

Related

MyBatis #Many / 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.

Spring Data MongoRepository save causing Duplicate Key error

Here is the Entity:
#Document
#Data
public class ApplicationUser {
private String name;
#Indexed(unique = true)
private String email;
private String organization = null;
// other fields
}
I fetch this user using their email and then change their name. I use the autowired instance of ApplicationUserRepository.
ApplicationUser applicationUser = applicationUserRepository.findByEmail("abc#gmail.com");
applicationUser.setName("John Doe 2");
Then I try to update this entity again in the database:
applicationUserRepository.save(applicationUser);
I get a duplicate key error on the field email. Why is this happening?
As far as I get from the documentation, the save method updates the same document if the ObjectId is the same. Since I haven't changed the objectId then why is it trying to create a new ApplicationUser during saving?
I got the solution.
When creating the entity, I have to explicitly declare the Id.
Here is the Entity:
#Document
#Data
public class ApplicationUser {
#Id
private ObjectId _id;
private String name;
#Indexed(unique = true)
private String email;
private String organization = null;
// other fields
}
I had similar issue where I was retrieving by id and then trying to update the retrieved POJO and then save it back with MongoRepository.save() call. It was on MongoDB 4.x with Spring Boot 2.1.0. I added the #Transactional annotation to my service method and everything worked like a charm. The duplicate key exception on id field was resolved.

Error when a form with a select field is submitted

In my current spring project, my forms are implement with a structure like this:
<form class="form" id="Pagina" role="form" method="POST" action="/loja/Pagina/cadastra" enctype="multipart/form-data">
...
</form>
and it's processed in server by this methos:
controller
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
#ResponseStatus(HttpStatus.CREATED)
public E cadastra(#ModelAttribute("object") E object, BindingResult result, #RequestParam(value="file", required=false) MultipartFile file, #RequestParam(value="icone", required=false) MultipartFile icone, #RequestParam(value="screenshot", required=false) MultipartFile screenshot[]) throws Exception {
E ret = serv.cadastra(object, file, icone, screenshot);
if (ret != null)
return ret;
else
throw new Exception();
}
service
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
#Transactional
public E cadastra(E e, MultipartFile file, MultipartFile icone, MultipartFile[] screenshot) {
return dao.persist(e);
}
My problem it's when the form have a field like this:
<label>pagina</label>
<select name="pagina.id" class="form-control select" data-lista="/loja/Pagina/listagem.json">
...
</select>
<label>produto</label>
<select name="produto.id" class="form-control select" data-lista="/loja/Produto/listagem.json">
...
</select>
which maps a atribute like this in the entiy class:
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="pagina_mae", nullable = true)
#Order(value=5)
#Select(name="pagina", ordem = 5)
#Sidebar
private Pagina pagina;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="produto_mae", nullable = true)
#Order(value=6)
#Select(name="produto", ordem = 6)
#Sidebar
private Produto produto;
Where the options inside looks like this:
<option value="">.</option>
<option value="...">...</option>
If I submit the form with the blank option selected, I get this error:
object references an unsaved transient instance - save the transient instance before flushing: com.spring.loja.model.pagina.persistence.model.Pagina; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.spring.loja.model.pagina.persistence.model.Pagina
but if, for instance, insert a record manually in the database (in my case, using pgAdmin3), and select this item in the select, the form is submitted without errors.
Anyone can tell me how I fix that, to allow me submit the form with or without selected data from the <select>.
UPDATE
code for the class Pagina:
#Entity
#Table(name="pagina")
#MainForm(grupo = 2, icone = "file")
public class Pagina extends ModelEntity {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "nome", unique = true)
#Order(value=1)
#Input(type="hidden", name="nome", ordem = 1)
private String nome;
#Column(name = "titulo", nullable = false)
#Order(value=2)
#Input(name="titulo", ordem = 2)
private String titulo;
#Column(name = "descricao", length=65535)
#Order(value=4)
#Textarea(name="descricao", ordem = 4)
private String descricao;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="pagina_mae", nullable = true)
#Order(value=5)
#Select(name="pagina", ordem = 5)
#Sidebar
private Pagina pagina;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="produto_mae", nullable = true)
#Order(value=6)
#Select(name="produto", ordem = 6)
#Sidebar
private Produto produto;
}
UPDATE 2
PaginaEditor.java
#Component
public class PaginaEditor extends PropertyEditorSupport {
#Inject
private PaginaService paginaService;
#Override
public void setAsText(String text) {
if (!text.isEmpty()) {
Pagina pagina = paginaService.getObject(text);
setValue(pagina);
}
}
}
method added to my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Pagina.class, new PaginaEditor());
}
Selects are tricky ones in Spring MVC.
I think your problem is that when your main entity is getting to the data layer to be persisted, the relationship is not there.
Try to debug and check if affirmation above is true.
There are two ways to get this sorted.
Let's assume a Company / Contact relationship in a contacts system.
A company has many contacts and a contact has one company.
Company snippet.
// package declaration imports and all
#Entity
#Table(name = "company")
public class Company {
private String name;
#OneToMany(mappedBy = "company")
private List<Contact> contacts = new ArrayList<Contact>();
// getter and setters and any extra stuff you fancy putting here
}
Contact snippet
// package declaration imports and all
#Entity
#Table(name = "contact")
public class Contact {
private String name;
#ManyToOne
private Company company;
// getter and setters and any extra stuff you fancy putting here
}
And a jsp snippet with the select.
We assume there is a "contact" object and a list of customers in the model.
<form:form modelAttribute="contact">
<form:input path="name" />
<form:select path="customer" items="${customers}" itemValue="id" itemLabel="name" />
</form:form>
With this code in place, you can use a PropertyEditor like this.
#Component
public class CustomerEditor extends PropertyEditorSupport {
#Inject
private CustomerService customerService;
#Override
public void setAsText(String text) {
if (StringUtils.isNotBlank(text)) {
Customer customer = this.customerService.findById(Integer
.valueOf(text));
this.setValue(customer);
}
}
}
and register in your Spring Context like this.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Customer.class, this.customerEditor);
}
What happens here is that, whenever Spring finds an Object of type Customer, it will use the PropertyEditor to convert the (in this case) Id to the object and by the type the contact (in this case) gets to the data layer (Hibernate), the proper Customer entity will be there waiting as happy as Larry.
That is automagic way to do it.
Another way to do it is to create a form/DTO or whatever you would like to call and add the fields including a customerId field (in this case) and convert your self before you save your entity.
I hope I understood your problem right because it took me quite a few minutes to write this... :)

JPQL Special Query

I have two entity bean :
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<Comment>();
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
and my Comment class is like this :
#Entity
public class Comment {
#Id
#GeneratedValue
private Long id;
private String title;
private String content;
#ManyToOne
private User user
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
now I know that I can get the User Object from session and set the user for my comment like this in order to be able to use the join feature in JPA:
commentObject.setUser(TheSessionGrabedUserObject/UserObjectWhichHasFetchedFromDbUsingUserId);
but as long as I have the userId for my user Object I do not need to do this.
I'm looking for a way to insert this foreignKey into my comment table without getting the User Object from session or maybe query to database to fetch it first !
how I'm gonna do it using JPQL ?
You can use the entityManager.getReference() method. In your case:
entityManager.getReference(User.class, userId);
This will not perform any DB query, but will give you a User instance with only the ID populated, and you can pass that to commentObject.setUser().

hibernate projection NumberFormatException for input string

I am using Spring MVC + Hibernate
Generic Dao
// getAll
#SuppressWarnings("unchecked")
public <T> List<T> getAll(Class<T> entityClass) throws DataAccessException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(entityClass);
return criteria.list();
}
#Controller
#RequestMapping(value = "/genCompanyInfoUpdate", method = RequestMethod.POST)
public String genCompanyInfoUpdate(Model model) {
List<GenCountryModel> countryList=pt.getAll(GenCountryModel.class);
List<GenCurrencyModel> currencyList=pt.getAll(GenCurrencyModel.class);
GenCompanyInfoModel companyInfo=pt.getById(GenCompanyInfoModel.class, 1);
model.addAttribute("countryList", countryList);
model.addAttribute("currencyList", currencyList);
model.addAttribute("companyInfo", companyInfo);
return "gen/genCompanyInfoUpdate";
}
JSP
<c:if test="${not empty currencyList}">
<c:forEach items="${currencyList}" var="get" varStatus="counter">
<ct:Options setValue="${get.id}" setName="${get.isoCode}" selected="${companyInfo.genCurrencyModel.id}" setState="1" />
</c:forEach>
</c:if>
All working well but when I change and use Projection in Method as the following , then it give exception
java.lang.numberformatexception for input string id
java.lang.numberformatexception for input string isoCode
Changes: ProjectionList use in Method
#SuppressWarnings("unchecked")
public <T> List<T> getAll(Class<T> entityClass, String[] nameList) throws DataAccessException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(entityClass);
ProjectionList pl = Projections.projectionList();
for (int i=0; i<nameList.length; i++) {
pl.add(Projections.property(nameList[i].toString()));
}
criteria.setProjection(pl);
return criteria.list();
}
Changes in #Controller passing List [GenCurrencyModel]
#RequestMapping(value = "/genCompanyInfoUpdate", method = RequestMethod.POST)
public String genCompanyInfoUpdate(Model model) {
String []list={"id","isoCode"};
List<GenCountryModel> countryList=pt.getAll(GenCountryModel.class);
List<GenCurrencyModel> currencyList=pt.getAll(GenCurrencyModel.class,list);
GenCompanyInfoModel companyInfo=pt.getById(GenCompanyInfoModel.class, 1);
model.addAttribute("countryList", countryList);
model.addAttribute("currencyList", currencyList);
model.addAttribute("companyInfo", companyInfo);
return "gen/genCompanyInfoUpdate";
}
Same JSP
<c:if test="${not empty currencyList}">
<c:forEach items="${currencyList}" var="get" varStatus="counter">
<ct:Options setValue="${get.id}" setName="${get.isoCode}" selected="${companyInfo.genCurrencyModel.id}" setState="1" />
</c:forEach>
</c:if>
GenCurrencyModel
public class GenCurrencyModel implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "CURRENCYID")
#GeneratedValue
private long id;
#Column(name = "CODE")
private String currencyCode;
#Column(name = "DESCRIPTION")
private String currencyDesc;
#Column(name = "ISACTIVE")
private int isActive;
#Column(name = "MADELETE")
private int markAsDelete;
#Column(name = "ISOCODE")
private String isoCode;
#Column(name = "CURRENCYUNIT")
private String currencyUnit;
#Column(name = "CONNECTOR")
private String connector;
#Column(name = "SUBUNIT")
private String subUnit;
#Column(name = "RECENTUSERID")
private long recentUserId;
#Column(name = "RECENTUSERIP")
private String recentUserIp;
#Column(name = "DATETIME")
private Date dateTime;
#Column(name = "ISUPDATED")
private int isUpdated;
private GenCompanyInfoModel genCompanyInfoModel;
public GenCurrencyModel() {
super();
}
//Getter Setter
}
I check the query from log file . it successfully execute
and when I remove the following line from jsp page, then there is no any exception
<ct:Options setValue="${get.id}" setName="${get.isoCode}"
Note: ct:Options is a custom JSP tag, that just print values, nothing special
After Projection the result of query is as follow
Hibernate: select this_.CURRENCYID as y0_, this_.ISOCODE as y1_ from GENCURRENCY this_
and both returning the list , and I have check both of size(), the size is also same !
Update me !
Typically, when using a projection list of specific properties in Hibernate, you won't be able to cast the query result as an entity type, at least not in the older versions of Hibernate I'm familiar with (i.e. 3.2.x). Instead, the default return type will be a List<Object[]> (when calling Criteria#list), where each array represents a tuple of the properties you specified in the projection list. (You can tell Hibernate to change the return type by giving the Criteria a ResultTransformer, but that may cause more confusion.) So instead of expecting partially-hydrated entities of type T and calling its getter methods (via JSTL expression), expect an array of Objects and get each property value by index (based on the order of the properties in the projection list).
Otherwise, it appears that you're passing the string values "id" and "isoCode" to your ct tag library (instead of the id and isoCode field values that you want), which I assume is expecting strings that can be parsed into numbers using something like Integer#parseInt(String), and this is causing the NumberFormatExceptions.
If this doesn't help, can you please provide more information? Specifically:
What are the property names you're specifying in the projection list?
What object types are those properties mapped as in the entity class? Providing the full entity mapping would help.
Is the ct:Options a custom JSP tag? If so, can you provide the logic of the tag class?

Resources