I have problem with JPA provided by Hibernate.
Here are my entities:
#Entity
public class Action {
#Id
#GeneratedValue
private Integer id;
#Column
private String name;
#JoinColumn
#ManyToOne
private User user;
public Integer getId() { return this.id; }
protected void setId(Integer id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public void getUser() { return this.user; }
protected void setUser(User user) { this.user = user; }
}
#Entity
public class User {
#Id
#GeneratedValue
private Integer id;
#Column
private String name;
#OneTomany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Action> actions = new ArrayList<>();
public Integer getId() { return this.id; }
protected void setId(Integer id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public void addAction(Action action) {
action.setUser(this);
actions.add(action);
}
public void removeAction(Action action) {
actions.remove(action);
action.setUser(null);
}
public List<Action> getActions() { return Collections.unmodifiableList(actions); }
}
Here is my controller code:
#Controller
#RequestMapping("/user/{username}")
#SessionAttributes("user")
public class ActionController {
#Autowired
private TestService service;
#ModelAttribute("user")
public User contextRegistration(#PathVariable("username") String username) {
return this.service.getByName(username);
}
#RequestMapping("")
public String userView() {
return "user";
}
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String addAction(#ModelAttribute Action action) {
return "addAction";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String postAddAction(#Valid Action action, BindingResult result, User user, SessionStatus sessionStatus) {
if(result.hasErrors()) {
return "addAction";
}
user.addAction(action);
this.service.saveAction(action);
sessionStatus.setComplete();
return "redirect:/user/{username}";
}
}
When method postAddAction is executed I got the following error:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : pl.zaprogramowany.test.model.Action.user -> pl.zaprogramowany.test.model.User
in line where method saveAction is executed.
My code is modeled on this code
https://github.com/spring-projects/spring-petclinic/blob/master/src/main/java/org/springframework/samples/petclinic/web/PetController.java#L79 but I don't know why I have this error because my code is pretty similar to this.
Can anybody help?
#Edit
I'm using spring Jpa repositories and code in service layer is very simple.
#Transactional(readyOnly = true)
public User getByName(String name) {
return this.userRepository.getByName(name);
}
#Transactional
public void saveAction(Action action) {
this.actionRepository.save(action);
}
This problem arises when you dont include cascade="all" (if using xml) or cascade=CascadeType.ALL (if using annotations) on your collection mapping.
Which in your case exists but at the user entity but not at the action entity
hence we need to see how are we saving the action entity on the basis of which we can decide if we need to add cascade on action entity or not
Your EntityManager is not aware of the User instance that is injected in your method. Then, when you try to save the Action instance with a reference to unaware User instace you get the transient object exception.
I don't know exactly how your User instance is injected but with that instance you should use entity manager to find it from the database. Doing this, your entity manager is aware of which user you are trying to use. Take a look to this post
Related
I am trying to get the following Json into POJOS using #RequestBody Instance instance
{
"service_id": "service-id-here",
"plan_id": "plan-id-here",
"context": {
"platform": "cloudfoundry",
"some_field": "some-contextual-data"
},
"organization_guid": "org-guid-here",
"space_guid": "space-guid-here",
"parameters": {
"agent_name": 1,
"url": "foo",
"credential": "asdasd",
"ia_url": "asdasd"
}
}
Below are my POJOs
Instance
public class Instance {
#JsonProperty(value = "service_id")
String serviceId;
#JsonProperty(value = "plan_id")
String planId;
//TODO : Replace with Context class when the spec defines things clearly
#JsonProperty(value = "context")
Object context;
#JsonProperty(value = "organization_guid")
String organizationGuid;
#JsonProperty(value = "space_guid")
String spaceGuid;
#JsonProperty(value = "parameters")
Parameters parameters;
}
Parameters
public class Parameters {
#JsonProperty(value = "agent_name")
String agentName;
#JsonProperty(value = "url")
String url;
#JsonProperty(value = "credential")
String credential;
#JsonProperty(value = "ia_url")
String iaUrl;
}
I use #JsonProperty everywhere. Is there any way to get underscore separated json keys into java's naming convention for variables (Camelcase)??
I tried using #JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) to my POJO classes instead of the #JsonProperty for each parameter. I just get an empty json {} in instance. What am I missing here?
Yes, is this possible using PropertyNamingStrategy class through JsonNaming annotation
Ex:
#JsonNaming(PropertyNamingStartergy.LowerCaseWithUnderscoresStrategy.class)
class Class_name{
...
}
//----
The below code has updated. In that code am using
PropertyNamingStrategy.SnakeCaseStrategy
Working code (TESTED).
Getters and setters are important for this to work. But #JsonProperty does not require them
User.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class User {
private int id;
private String beanName;
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Role.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Role {
private int id;
private String roleName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Here is the Controller
#RestController
#RequestMapping("/test")
public class NamingController {
#RequestMapping(value="/jsontopojo", method = RequestMethod.POST)
public ResponseEntity<User> jsontopojo(#RequestBody User nam) {
return new ResponseEntity<User>( nam, HttpStatus.OK);
}
}
I have entity as follow.
#Entity
#Table(name = "BankProduct")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private ProductUseType type;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne
private ProductSerial serial;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductUseType getType() {
return type;
}
public void setType(ProductUseType type) {
this.type = type;
}
public ProductSerial getSerial() {
return serial;
}
public void setSerial(ProductSerial serial) {
this.serial = serial;
}
}
My controller is :
#RestController
public class DEmoController {
#Autowired
private ProductRepository productRepository;
#GetMapping("/products")
public Returns products() {
return new Returns(ReturnStatus.SUCCESS.getStatus(), productRepository.findAll(), null);
}
}
It will load both of type and serial of product.
Can I only load type but not to load serial?
I don't want to add fetch=FetchType.LAZY to serial, because if next time I want to load serial but not to load type, it will be terrible.
Check the Projection interface
Create a interface ProductProjection
interface ProductProjection {
String getName();
String getType();
}
and add a method in you Repository
List<ProductProjection> findAllProjection()
That's the whole point of fetch=FetchType.LAZY. It'll not load any of your types/fields until you ask for them explicitly.
Take a look at this question: Link
While there is plenty of information around on how to model, in JPA (2), a one-to-one relationship OR an entity having a natural key, I haven't been able to find a clear / simple answer to how to model the situation where we have both, i.e. a one-to-one relationship where the parent table has a natural key. It could obviously be that I might have missed such a tutorial; if so, pointing me to one could also be the answer.
And, as many times with JPA and noobs such as I, the moment one needs a bit more than the most basic model, one can quickly hit the wall.
Hence, considering the following DB model:
What would be the corresponding JPA-annotated object model? (I'm sparing you guys of the things I've tried since I don't want to influence the answer...)
Performance recommendations are also welcome (e.g. "a one-to-many could perform faster", etc.)!
Thanks,
The composite identifier is built out of two numerical columns so the mapping looks like this:
#Embeddable
public class EmployeeId implements Serializable {
private Long companyId;
private Long employeeId;
public EmployeeId() {
}
public EmployeeId(Long companyId, Long employeeId) {
this.companyId = companyId;
this.employeeId = employeeId;
}
public Long getCompanyId() {
return companyId;
}
public Long getEmployeeId() {
return employeeId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof EmployeeId)) return false;
EmployeeId that = (EmployeeId) o;
return Objects.equals(getCompanyId(), that.getCompanyId()) &&
Objects.equals(getEmployeeId(), that.getEmployeeId());
}
#Override
public int hashCode() {
return Objects.hash(getCompanyId(), getEmployeeId());
}
}
The parent class, looks as follows:
#Entity(name = "Employee")
public static class Employee {
#EmbeddedId
private EmployeeId id;
private String name;
#OneToOne(mappedBy = "employee")
private EmployeeDetails details;
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public EmployeeDetails getDetails() {
return details;
}
public void setDetails(EmployeeDetails details) {
this.details = details;
}
}
And the child like this:
#Entity(name = "EmployeeDetails")
public static class EmployeeDetails {
#EmbeddedId
private EmployeeId id;
#MapsId
#OneToOne
private Employee employee;
private String details;
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
this.id = employee.getId();
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
And everything works just fine:
doInJPA(entityManager -> {
Employee employee = new Employee();
employee.setId(new EmployeeId(1L, 100L));
employee.setName("Vlad Mihalcea");
entityManager.persist(employee);
});
doInJPA(entityManager -> {
Employee employee = entityManager.find(Employee.class, new EmployeeId(1L, 100L));
EmployeeDetails employeeDetails = new EmployeeDetails();
employeeDetails.setEmployee(employee);
employeeDetails.setDetails("High-Performance Java Persistence");
entityManager.persist(employeeDetails);
});
doInJPA(entityManager -> {
EmployeeDetails employeeDetails = entityManager.find(EmployeeDetails.class, new EmployeeId(1L, 100L));
assertNotNull(employeeDetails);
});
doInJPA(entityManager -> {
Phone phone = entityManager.find(Phone.class, "012-345-6789");
assertNotNull(phone);
assertEquals(new EmployeeId(1L, 100L), phone.getEmployee().getId());
});
Code available on GitHub.
I'm not sure if the title is right... But here is my problem
This is my bean class
#Entity
#Table(name = "Exercise")
public class Exercise {
private IntegerProperty exerciseID;
private ObjectProperty<String> name;
private ObjectProperty<ExerciseCategory> category;
private ObservableList<Parameter> parameters;
public Exercise(int exerciseID, String name, ExerciseCategory category){
this.exerciseID = new SimpleIntegerProperty(exerciseID);
this.name = new SimpleObjectProperty<>(name);
this.category = new SimpleObjectProperty<>(category);
parameters = FXCollections.observableArrayList();
}
public Exercise(){
this(0,null, null);
}
public Exercise(String name, ExerciseCategory category){
this(0, name, category);
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getExerciseID() {
return exerciseID.get();
}
public IntegerProperty exerciseIDProperty() {
return exerciseID;
}
public void setExerciseID(int exerciseID) {
this.exerciseID.set(exerciseID);
}
#Column(name = "name", nullable = false)
public String getName() {
return name.get();
}
public ObjectProperty<String> nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
#OneToOne
public ExerciseCategory getCategory() {
return category.get();
}
public ObjectProperty<ExerciseCategory> categoryProperty() {
return category;
}
public void setCategory(ExerciseCategory category) {
this.category.set(category);
}
public ObservableList<Parameter> getParameters() {
return parameters;
}
public void setParameters(ObservableList<Parameter> parameters) {
this.parameters = parameters;
}
}
One exercise can have one more parameters. This mapping is saved in the ExerciseParameter table, which looks like this:
ExerciseParameter
ExerciseID int(11) PK
ParameterID int(11) PK
My question is, how do I map this in the Exercise class? Because I don't want to make a ExerciseParamter class...
Thank you!
It is a many-to-many relationship. You can use #ManyToMany annotion to do that.
Add following Annotation into getParameters method of Employee Entity
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="ExerciseParameter",
joinColumns={#JoinColumn(name="ExerciseID ")},
inverseJoinColumns={#JoinColumn(name="ParameterID")})
And also add following Annotation into getEmployees method of Parameter entity. (You have not presented the Parameter Entity. I assume that Parameter class contains the getEmployees method.)
#ManyToMany(mappedBy="parameters")
This is my Controller method, i am trying to read my database by providing zip, cityname and province name.
#RequestMapping(value = "/retrieve", method = RequestMethod.GET)
public #ResponseBody String retrieveObjectThroughAjax(ModelMap model){
//Calling Service Method to read data according to zip,cityName and province provide
PropertyItems propertyItems=getPropertyTypeandAddressService.readAddressFromZip("H2H-
2N3","Montreal","Quebec");
Gson gson = new Gson();
String json = null;
try{
json = gson.toJson(propertyItems); // serializing object
}catch(Exception e){
logger.error(Constants.METHOD_INSIDE_MESSAGE +"getAuthors",e);
}
logger.debug(json);
return json;
}
}
Service Method
#Service
public class GetPropertyTypeandAddressServiceImpl implements GetPropertyTypeandAddressService{
#Autowired
private GetPropertyTypeandAddressDAO getPropertyTypeandAddressDAO;
#Transactional
public PropertyItems readAddressFromZip(String zipCode,String cityName,String provinceName){
PropertyItems propertyItems=getPropertyTypeandAddressDAO.getAddressFromZip(zipCode, cityName, provinceName);
Hibernate.initialize(propertyItems);
return propertyItems;
}
}
DAO Method
#Repository
public class GetPropertyTypeandAddressDAOimp implements GetPropertyTypeandAddressDAO{
#Autowired
private SessionFactory sessionFactory;
#Override
public PropertyItems getAddressFromZip(String zipCode,String cityName,String provinceName) {
PropertyItems propertyitems = new PropertyItems();
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PropertyItems.class,"propertyItemsClass");
if(zipCode != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.zip",zipCode));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(cityName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.cityname",cityName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(provinceName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.provinces","provinces"));
criteria.add(Restrictions.eq("provinces.provinceName",provinceName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
return propertyitems;
}
}
Console Error
09:53:56,988 ERROR HelloController:567 - Inside Method: getAuthors org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.agilemaple.common.entity.Property.propertyType, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
As requested my Property Items Look like this
Entity:
Propert Items
#Entity
#Table(name="web_property_item")
public class PropertyItems {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_type_id")
private PropertyType propertyType;
#OneToOne(mappedBy="propertyItems",cascade=CascadeType.ALL)
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PropertyType getPropertyType() {
return propertyType;
}
public void setPropertyType(PropertyType propertyType) {
this.propertyType = propertyType;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
Entity : Property Type
#Entity
#Table(name="web_property_type")
public class PropertyType {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_id")
private Property property;
#OneToMany(fetch = FetchType.LAZY,mappedBy="propertyType", cascade=CascadeType.ALL)
private Set<PropertyItems> propertyItems;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public Set<PropertyItems> getPropertyItems() {
return propertyItems;
}
public void setPropertyItems(Set<PropertyItems> propertyItems) {
this.propertyItems = propertyItems;
}
}
The problem in hibernate. Your field Set of properties has Lazy fetch method, it means that it will try to get when you call method get of this set. When u calling tojson methods, gson calls all get methods of object but in this moment hibernate session is close and hibernate can't open it in controller. I've faced with the same problem but directly on JSP. In a three weeks i resolved it by one more property for hibernate ( in your case) and I write code to opening session in view interceptor. I'm underground just right now, so I can't show property, but in a hour I will edit this answer and add property.
Added:
I remembered ! property is: hibernate.enable_lazy_load_no_trans = true
If it won't help, I will add code of opensessioninviewinterceptor.
#Override
public void addInterceptors(InterceptorRegistry registry) {
OpenSessionInViewInterceptor sessionInViewInterceptor = new OpenSessionInViewInterceptor();
sessionInViewInterceptor.setSessionFactory(sessionFactory());
}