Unable to serialize custom object activeandroid - sqlite

I am trying to store some custom object as a blob in SqlLite db. The object is a field of a class extending Model. All other fields (of primitive types) go successfully in the DB, but the custom one - it is null always.
#Table(name = "data")
public class Data extends Model {
#Column(name = "number")
private int number;
#Column(name = "blob")
private Contact blob;
...
This is how i store the entity
Data data = new Data(0, new Contact(id, name, number));
data.save();
Here is the contact class
public class Contact {
private String id;
private String name;
private String number;
...
I believe a TypeSerializer is needed, so I've created one.
public class ContactSerializer extends TypeSerializer {
private static final String ELEMENTS_DELIMITER = ";";
#Override
public Object deserialize(Object asString) {
String[] afterSplit = ((String) asString).split(ELEMENTS_DELIMITER);
return new Contact(afterSplit[0], afterSplit[1], afterSplit[2]);
}
#Override
public Class<?> getDeserializedType() {
return Contact.class;
}
#Override
public SerializedType getSerializedType() {
return SerializedType.STRING;
}
#Override
public Object serialize(Object asContact) {
Contact temp = (Contact) asContact;
return temp.getId() + ELEMENTS_DELIMITER + temp.getName() + ELEMENTS_DELIMITER
+ temp.getNumber();
}
}
When i query the db I got object with this particular field "Contact" as null always. Where might be the problem? Do I need to specify which is the TypeSerializer for my object? Or the implementation of TypeSerializer I've created is wrong?

You also need to extent Contact from Model:
#Table(name = "contact")
public class Contact extends Model{
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
#Column(name = "number")
private String number;
}
Now everything should work out of the box. It's a bit late for a response but perhaps I will help someone else.

Related

Spring JPA: Adding an entity with current user ID as foreign key

I'm using a Thymeleaf HTML registration form and simple save/update method to save/update a 'dish' object to a mySQL database. Restaurant Id is a foreign key for the 'dish' but using the below methods it saves as 'null',
I would like to make it so that the Restaurant id of the currently logged in restaurant owner saves automatically when they add a dish.
Is there an uncomplicated way to do this? The closest tutorial I've found on Youtube involves using JSON requests in Postman and I've had issue adapting that to a HTML registration form in the past.
I'm quite new to all of this so any help would be very much appreciated!
See Dish class:
package com.bron.demoJPA.appuser;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#ToString(exclude = "reqlist")
public class Dish {
#Id
#SequenceGenerator(name = "dish_sequence", sequenceName = "dish_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dish_sequence")
#Column(name = "dish_Id")
private Long dishId;
#Column(name = "dish_name")
private String dname;
#Column(name = "dish_description")
private String description;
#Column(name = "dish_price")
private double price;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Rest_ID", referencedColumnName = "Rest_ID")
private AppUser app;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "dish_requirment_mapping", joinColumns = #JoinColumn(name = "dish_Id", referencedColumnName = "dish_Id"), inverseJoinColumns = #JoinColumn(name = "Require_ID", referencedColumnName = "Require_ID"))
private List<Requirments> reqlist;
public void addRequirments(Requirments req) {
if (reqlist == null)
reqlist = new ArrayList<>();
reqlist.add(req);
}
}
See AppUser(restaurant owner) Class
#Column(name = "Rest_Password")
private String password;
#Column(name = "Rest_Email_Address")
private String email;
#Enumerated(EnumType.STRING)
private AppUserRole appUserRole;
private Boolean locked = false;
// don't enable user until email verification
private Boolean enabled = false;
public AppUser(String restname, String email, String pass, AppUserRole app) {
this.restaurantName = restname;
this.email = email;
this.password = pass;
this.appUserRole = app;
}
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(appUserRole.name());
return Collections.singletonList(authority);
}
#Override
public String getUsername() {
return email;
}
#Override
public String getPassword() {
return password;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return !locked;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return enabled;
}
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "openingHourID", referencedColumnName = "OpeningHour_ID")
private OpeningHour opening;
}
See Controller class:
package com.bron.demoJPA.conroller;
#Controller
public class DishController {
//display list of employees
#Autowired
private DishService dishService;
#GetMapping("/dish")
public String viewHomePage(Model model) {
model.addAttribute("listDish", dishService.getAllDish());
return "index";
}
#GetMapping("/showNewDishForm")
public String showNewDishForm(Model model) {
// Create model attribute to bind form data
Dish dish = new Dish();
model.addAttribute("dish", dish);
return "new_dish";
}
#PostMapping("/saveDish")
public String saveDish(#ModelAttribute("dish") Dish dish) {
// save dish to database
dishService.saveDish(dish);
return "redirect:/dish";
}
#GetMapping("/showFormForUpdate/{dishId}")
public String showFormForUpdate(#PathVariable(value = "dishId") long dishId, Model model) {
// get dish from service
Dish dish = dishService.getDishByDishId(dishId);
// set dish as model to pre-populate the form data
model.addAttribute("dish", dish);
return "update_dish";
}
}
See Service implementation
package com.bron.demoJPA.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bron.demoJPA.appuser.Dish;
import com.bron.demoJPA.repository.DishRepository;
#Service
public class DishServiceImpl implements DishService {
#Autowired
private DishRepository dishRepository;
#Override
public List<Dish> getAllDish() {
return dishRepository.findAll();
}
#Override
public void saveDish(Dish dish) {
this.dishRepository.save(dish);
}
#Override
public Dish getDishByDishId(long dishId) {
Optional<Dish> optional = dishRepository.findById(dishId);
Dish dish = null;
if (optional.isPresent()) {
dish = optional.get();
} else {
throw new RuntimeException("Dish not found for: " + dishId);
}
return dish;
}
}
See Service class
public interface DishService {
List<Dish> getAllDish();
void saveDish(Dish dish);
Dish getDishByDishId(long dishId);
}
Can you make sure Dish's "app" attribute is being set correctly before trying to save it?
If it's null or it's a brand new instance of AppUser class it makes sense that when trying to match and persist it ends up on null.
Greetings!

Unable to get insert, update and delete to work with spring-data-jpa

I am trying to create a web service that performs basic CRUD operations written using spring boot 2. The select operation works fine, however the insert, delete and update operations have no effect as their query is not getting generated and executed.
I have looked through different examples but I am unable to figure out any issues. The major concern for me is the fact that not even a query is being triggered for insert, delete or update operations.
Student Entity
#Entity
#Table(name = "student")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Student {
#Id
#NotNull
#Column(name = "id")
private int id;
#NotNull
#Column(name = "name")
private String name;
#Column(name = "course")
private String course;
public Student(int id, String name, String course) {
this.id = id;
this.name = name;
this.course = course;
}
public Student(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
#Override
public String toString() {
return "Student{ id=" + id + ", name='" + name + '\'' + ", course='" + course + '\'' + '}';
}
}
StudentDaoImpl
#Repository
#Transactional
public class StudentDaoImpl implements StudentDao {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Override
public List<Student> fetchAllStudents() {
Session session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> root = cq.from(Student.class);
CriteriaQuery<Student> all = cq.select(root);
List<Student> solution = session.createQuery(all).getResultList();
session.close();
return solution;
}
#Override
public Student deleteStudent(Integer id) {
Session session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
Student student = session.load(Student.class, id);
if (student != null){
session.delete(student);
session.close();
}
return student;
}
#Override
public Student fetchForId(Integer id){
Session session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
Student student = session.load(Student.class, id);
session.close();
return student;
}
#Override
public Student insertStudent(Student student) {
Session session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
session.save(student);
session.close();
return student;
}
#Override
public Student updateStudent(Student student) {
Session session = entityManagerFactory.unwrap(SessionFactory.class).openSession();
Student studentCheck = session.load(Student.class, student.getId());
if (studentCheck != null) {
session.saveOrUpdate(student);
session.close();
}
return student;
}
}
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=
spring.jpa.database = MYSQL
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
Edit
Replacing EntityManagerFactory with EntityManager( + Persistent Context Annotation) worked for me. However I still haven't figured why persistence worked for EntityManager.
If it's not strictly important, you can do it using NativeQuery and its executeUpdate API:
String query = "insert into student values(1,?)";
em.createNativeQuery(query)
.setParameter(1, "Tom")
.executeUpdate();
I would like to suggest this repository
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
Probably you have to change the id of Student from int to Integer.
And this repository has the methods for retrieving, updating, creating and deleting.
Let's say that you want to use this repository in a Service, you can do that like this:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
#Transactional(rollbackFor = Exception.class)
public class StudentService {
#Autowired
private StudentRepository studentRepository;
......
}

Unable to perform LAZY FETCH operation using JPA in one-to-one relationship

I am trying to lazily fetch PetDetails entity using JPA. However, I get LazyInitialization Exception. I read many solutions for this exception and came to find solution using JOIN FETCH in JPQL. Also people recommended using Criteria queries. However, I am trying to look for a solution if there is a way I can fetch the PetDetails entity the way I want without using queries directly or depending on Criteria API or without using EAGER FETCH. I might be missing something. I would appreciate for any help or suggestion. Below are the code samples:
1. Controller class:
#Controller
public class PetController {
private static Logger LOGGER = Logger.getLogger(PetController.class);
#Autowired
private PetService petService;
#RequestMapping(value = "/", method = RequestMethod.GET)
public void manageAndDisplayPet() {
PetDetails petDetails = new PetDetails();
petDetails.setName("DOG");
Pet pet = new Pet(petDetails);
// save
petService.savePet(pet);
// retrieve
LOGGER.debug("**********************" + petService.getPet());
LOGGER.debug("**********************" + pet.getPetDetails());
}
}
2. PetService class:
#Service
public class PetService {
#Autowired
private PetDAO petDAO;
#Transactional
public void savePet(Pet pet) {
petDAO.savePet(pet);
}
#Transactional
public Pet getPet() {
return petDAO.getPet();
}
}
3. PetDAO class
#Repository
#EnableTransactionManagement
public class PetDAO {
#PersistenceContext(unitName = "petcontext")
private EntityManager entityManagerFactory;
public void savePet(Pet pet) {
entityManagerFactory.persist(pet);
}
public Pet getPet() {
Pet pet = (Pet) entityManagerFactory.find(Pet.class, 1);
return pet;
}
}
4. Pet Entity:
#Entity
#Table(name = "t_pet")
public class Pet {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JoinColumn(name = "pet_details")
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private PetDetails petDetails;
public Pet() {
}
public Pet(PetDetails petDetails) {
this.petDetails = petDetails;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public PetDetails getPetDetails() {
return petDetails;
}
public void setPetDetails(PetDetails petDetails) {
this.petDetails = petDetails;
}
#Override
public String toString() {
return "Pet [id=" + id + ", petDetails=" + petDetails + "]";
}
}
5. PetDetails Entity:
#Entity
#Table(name = "pet_details")
public class PetDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "pet_name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "PetDetails [id=" + id + ", name=" + name + "]";
}
}
Thank you for your help.
Easy thing you can do is to call pet.getPetDetails() inside PetService#getPet. This solution is not very clear, but it will force JPA to fetch entity too. This is solution for your question, but not the good way anyways.
What is the good way?
The good way may depend on your particular usecase:
If you need this details everytime — you should use EAGER_FETCH
If you need it time to time than good solution it to use JPQL with JOIN FETCH
But the best way is to select not entites, but DTOs, which will contain whole information which your application needs and not more. It may be achieved with SELECT NEW expression as described here

Spring-data-JPA with foreign key reference to "UserID" within Composite Key attempting to map to column "user" instead of UserID

I am attempting to connect to my database in a Spring MVC application. There are two tables. Users and Orders, Users has a primary key column: "userID", orders has a composite key from columns: "userID" and "orderID", where userID is a foreign key referencing the "userID" column in the Users table.
Here are my classes:
Order:
#Entity
#Table(name = "Orders")
#IdClass(OrderPK.class)
public class Order implements Serializable{
private static final Long serialVersionUID = 1L;
#EmbeddedId
private OrderPK orderPK;
//other properties
//no args and full args constructor
//getters and setters
//toString
}
OrderPK:
#Embeddable
public class OrderPK implements Serializable {
#Column(name = "orderID")
private Long orderID;
#ManyToOne
#JoinColumn(name = "userID")
private User user;
public OrderPK() {
}
public OrderPK(Long orderID, User user) {
this.orderID = orderID;
this.user = user;
}
public Long getOrderID() {
return orderID;
}
public void setOrderID(Long orderID) {
this.orderID = orderID;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof OrderPK)) return false;
OrderPK that = (OrderPK) o;
return Objects.equals(getOrderID(), that.getOrderID()) &&
Objects.equals(getUser(), that.getUser());
}
#Override
public int hashCode() {
return Objects.hash(getOrderID(), getUser());
}
}
User:
#Entity
#Table(name = "USERS")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="USER_SEQUENCE", sequenceName="USER_SEQUENCE")
#GeneratedValue(strategy=GenerationType.SEQUENCE,
generator="USER_SEQUENCE")
#Column(name = "userid")
private Long userId;
//other properties
//no args and full args constructor
//getters and setters
//toString
}
When I try to connect to the database I get the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Unable to find properties (orderID, user) in entity annotated with #IdClass:com.ex.evemarketback.domain.Order
...
Caused by: org.hibernate.AnnotationException: Unable to find properties (orderID, user) in entity annotated with #IdClass:com.ex.evemarketback.domain.Order
Any suggestions?
As you are using the #EmbeddedId, you do not need the #IdClass annotation:
#Entity
#Table(name = "Orders")
public class Order implements Serializable{
or if you want to keep the #IdClass:
// #Embeddable - no need for that
public class OrderPK implements Serializable {
private Long orderID;
private Long userId;
...
}
entity:
#Entity
#Table(name = "Orders")
#IdClass(OrderPK.class)
public class Order implements Serializable{
#Id
#Column(name = "orderID")
private Long orderID;
#Id
#Column(name = "userId", insertable=false, updatable=false)
private Long userId;
#ManyToOne
#JoinColumn(name = "userID")
private User user;

JsonMappingException: Can not deserialize instance of enum out of START_OBJECT token

My Json looks like following
{
name: "math",
code:null,
description:"Mathematics",
id:null,
name:"math",
noExam:null,
teacher:{
id: "57869ced78aa7da0d2ed2d92",
courseGroup:"LKG",
experties:[{type: "SOCIALSTUDIES", id: "3"}, {type: "PHYSICS", id: "4"}]
},
id:"57869ced78aa7da0d2ed2d92"
}
if you see my entity classes, I have a set of enums in Teacher.java
When I try to post this I get error
JsonMappingException: Can not deserialize instance of com.iris.fruits.domain.enumeration.Experties out of START_OBJECT token
I have tried almost all solutions out there, like DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, but without success.
public class Subject implements Serializable {
// all the other fields
#JoinColumn(name = "teacher_id")
private Teacher teacher;
// getter and setter
}
public class Teacher implements Serializable {
// all the other fields
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
#Enumerated(EnumType.STRING)
#Column(name = "experties")
#JsonProperty("experties")
private List< Experties> experties;
// getter and setter
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Experties implements Serializable {
MATH(1,"MATH"),
SCIENCE(2,"SCIENCE"),
SOCIALSTUDIES(3,"SOCIALSTUDIES"),
PHYSICS(4,"PHYSICS"),
CHEMISTRY(5,"CHEMISTRY");
#JsonSerialize(using = ToStringSerializer.class)
private String type;
#JsonSerialize(using = ToStringSerializer.class)
private Integer id;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
Experties(Integer id, final String type) {
this.id = id;
this.type = type;
}
}
You have this issue because you have a custom serializer in your enum (#JsonFormat(shape = JsonFormat.Shape.OBJECT)). Thus, to solve the issue, you need a custom deserializer.
You can define a custom deserializer using :
#JsonFormat(shape = JsonFormat.Shape.OBJECT) // custom serializer
#JsonDeserialize(using = MyEnumDeserializer.class) // custom deserializer
public enum Experties implements Serializable {
...
}
The custom deserializer being :
public static class MyEnumDeserializer extends JsonDeserializer<Experties> {
#Override
public Experties deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String type = node.get("type").asText();
return Stream.of(Experties.values())
.filter(enumValue -> enumValue.getType().equals(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("type "+type+" is not recognized"));
}
}
Of course, you can have another deserializer implementation (e.g. use the id field instead of the type field, check the coherence between the id and type fields).
Your class should match with the structure of the json. And in your input json shouldn't repeat keys.
I guess you classes, should like below:
public class Subject implements Serializable {
// all the other fields
String name;
String code;
String description;
String id;
String noExam;
#JoinColumn(name = "teacher_id")
private Teacher teacher;
// getter and setter
}
public class Teacher implements Serializable {
// all the other fields
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
#Enumerated(EnumType.STRING)
#Column(name = "experties")
#JsonProperty("experties")
private List< Experties> experties;
String courseGroup;
// getter and setter
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Experties implements Serializable {
MATH(1,"MATH"),
SCIENCE(2,"SCIENCE"),
SOCIALSTUDIES(3,"SOCIALSTUDIES"),
PHYSICS(4,"PHYSICS"),
CHEMISTRY(5,"CHEMISTRY");
#JsonSerialize(using = ToStringSerializer.class)
private String type;
#JsonSerialize(using = ToStringSerializer.class)
private Integer id;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
Experties(Integer id, final String type) {
this.id = id;
this.type = type;
}
}
Add JsonDeserialize annotation to experties setter in Teacher class:
#JsonDeserialize(using = EnumDeserializer.class)
public void setExperties(List experties){
//...
}
You have this issue because you have a custom serializer in your enum #JsonFormat(shape = JsonFormat.Shape.OBJECT). Jackson does not know how to deserialize object from json, into enum. You can create "creator" method to solve this easily:
public enum Experties implements Serializable {
#JsonCreator
public Experties(String type) {
// here you can use "type" attribute from json object to create your Enum instance. If you need, you can also add as parameter "Integer Id", as it is in your enum json representation.
return valueOf(type);
}
}

Resources