How is jackson serializing object outside of transition - spring-boot

This is a bit bizarre for me. My understanding is if you try to access entity outside of Hibernate session you should get an LazyInitializationException - no session, but I am not getting this exception. In fact it seems like I still have session continue to my controller outside of my service layer which has #Transactional annotation.
Question
Is this the way it should be? or there is something I didn't set up correctly?
Structure
User
|
Profile { hierarchies entity }
|
+-----+-----+
| |
Teacher Student
User
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
#OneToMany(fetch = FetchType.LAZY , mappedBy = "user")
#JsonManagedReference
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<Profile> profiles = new ArrayList<>();
Service
#Override
#Transactional
public User getUserById(Long id) {
Optional<User> optionalUser = userRepo.findById(id);
User user = optionalUser.get();
logger.info("\n User -> {}", user);
return user;
}
Controller :: Instead of thow Exception, or should return empty array. Hibernate perform a left join
#GetMapping(value="/{userId}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public User getUser(#PathVariable("userId") Long userId) {
logger.info("User Controller is called");
User user = userService.getUserById(userId);
logger.info("User is returned -> {}", user);
logger.info("Profiles -> {}", user.getProfiles()); // <-- Expecting this to throw LazyInitializationException
return user;
}
Console
018-05-31 15:17:35.462 INFO 15599 --- [nio-8080-exec-1] c.d.c.controller.UserController : User Controller is called
Hibernate:
select
user0_.id as id1_4_0_,
user0_.firstname as firstnam2_4_0_,
user0_.lastname as lastname3_4_0_
from
user user0_
where
user0_.id=?
2018-05-31 15:17:35.467 INFO 15599 --- [nio-8080-exec-1] c.d.c.services.UserServiceImpl :
User ->
User [id=1, firstname=Hendric, lastname=Rosenberg]
2018-05-31 15:17:35.467 INFO 15599 --- [nio-8080-exec-1] c.d.c.controller.UserController : User is returned ->
User [id=1, firstname=Hendric, lastname=Rosenberg]
Hibernate:
select
profiles0_.user_id as user_id2_3_0_,
profiles0_.id as id1_3_0_,
profiles0_.id as id1_3_1_,
profiles0_.user_id as user_id2_3_1_,
profiles0_1_.citizent_id as citizent1_2_1_,
profiles0_1_.profile_type as profile_2_2_1_,
profiles0_2_.license as license1_1_1_,
profiles0_2_.practitioner_type as practiti2_1_1_,
profiles0_2_.profile_type as profile_3_1_1_,
profiles0_2_.specialized as speciali4_1_1_,
case
when profiles0_1_.id is not null then 1
when profiles0_2_.id is not null then 2
when profiles0_.id is not null then 0
end as clazz_1_
from
profile profiles0_
left outer join
patient profiles0_1_
on profiles0_.id=profiles0_1_.id
left outer join
medical_profession profiles0_2_
on profiles0_.id=profiles0_2_.id
where
profiles0_.user_id=?
c.d.c.controller.UserController : User is returned -> [
Student [profileType=STUDENT, id=A1236578889],
Teacher [profileType=TEACHER, license=234SFLLWEKD32342]]

spring.jpa.open-in-view = true is set by default. So you won't get a LazyInitializationException
This property will register an OpenEntityManagerInViewInterceptor
Spring web request interceptor that binds a JPA EntityManager to the thread for the entire processing of the request. Intended for the "Open EntityManager in View" pattern, i.e. to allow for lazy loading in web views despite the original transactions already being completed.
This interceptor makes JPA EntityManagers available via the current thread, which will be autodetected by transaction managers. It is suitable for service layer transactions via JpaTransactionManager or JtaTransactionManager as well as for non-transactional read-only execution.
In contrast to OpenEntityManagerInViewFilter, this interceptor is set up in a Spring application context and can thus take advantage of bean wiring.

Related

Q: Transactional Code why does this work so well?

Hello my professionals I have a simple question here that I would like to beg to solve this..
this is an Entity of Member
#Entity
#Getter
#Builder
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#AllArgsConstructor
/*#ToString(of = {"id", "username", "age"})*/
public class Member {
#Id
/*#GeneratedValue(strategy = GenerationType.IDENTITY)*/
#Column(name = "member_id")
private Long id;
private String username;
private int age;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "member")
private List<Team> teams;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "member")
private List<Coach> coachs;
}
And this is an Entity of Coach
#Entity
#AllArgsConstructor
#Getter
#Builder
#Setter
#NoArgsConstructor
#ToString(of = {"id","name","career"})
public class Coach {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name= "coach_id")
private Long id;
#Column
private String name;
#Column
private String career;
#ManyToOne(fetch = FetchType.LAZY,cascade = ALL)
#JoinColumn(name = "member_id")
private Member member;
#OneToOne(fetch = FetchType.LAZY,cascade = ALL)
#JoinColumn(name = "team_id")
private Team team;
}
and This is Controller Code
#GetMapping("/member")
public void createUser(){
Member m = memberService.createMember();
Coach c = m.getCoachs().get(0);
log.info(c.getName());
}
and This is Service Code
private final MemberRepository memberRepository;
#Transactional
public Member createMember(){
return memberRepository.findMemberById(3L);
}
and the last this is RepositoryCode
Member findMemberById(Long id);
So my question is that when i printed out Coach's name at the controller on console
it printed out so well.
but what I know the Transaction is over from the service So the persistence container is closed that means coach name can't be imported cause it's LAZY loading and persistence container is closed but it was printed out well
I want to know the reason why ...
here are the console results Thanks !!
[2022-01-10 23:27:46.835] [http-nio-9000-exec-2] [] INFO o.a.c.c.C.[.[.[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[2022-01-10 23:27:46.835] [http-nio-9000-exec-2] [] INFO o.s.w.s.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
[2022-01-10 23:27:46.855] [http-nio-9000-exec-2] [] INFO o.s.w.s.DispatcherServlet - Completed initialization in 19 ms
Hibernate:
/* select
generatedAlias0
from
Member as generatedAlias0
where
generatedAlias0.id=:param0 */ select
member0_.member_id as member_i1_1_,
member0_.age as age2_1_,
member0_.username as username3_1_
from
member member0_
where
member0_.member_id=?
[2022-01-10 23:27:47.007] [http-nio-9000-exec-2] [4c0222d3] INFO p6spy - #1641824867007 | took 15ms | statement | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
/* select generatedAlias0 from Member as generatedAlias0 where generatedAlias0.id=:param0 */ select member0_.member_id as member_i1_1_, member0_.age as age2_1_, member0_.username as username3_1_ from member member0_ where member0_.member_id=?
/* select generatedAlias0 from Member as generatedAlias0 where generatedAlias0.id=:param0 */ select member0_.member_id as member_i1_1_, member0_.age as age2_1_, member0_.username as username3_1_ from member member0_ where member0_.member_id=3;
[2022-01-10 23:27:47.170] [http-nio-9000-exec-2] [4c0222d3] INFO p6spy - #1641824867170 | took 12ms | commit | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
;
Hibernate:
select
coachs0_.member_id as member_i4_0_0_,
coachs0_.coach_id as coach_id1_0_0_,
coachs0_.coach_id as coach_id1_0_1_,
coachs0_.career as career2_0_1_,
coachs0_.member_id as member_i4_0_1_,
coachs0_.name as name3_0_1_,
coachs0_.team_id as team_id5_0_1_
from
coach coachs0_
where
coachs0_.member_id=?
[2022-01-10 23:27:47.200] [http-nio-9000-exec-2] [4c0222d3] INFO p6spy - #1641824867200 | took 12ms | statement | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
select coachs0_.member_id as member_i4_0_0_, coachs0_.coach_id as coach_id1_0_0_, coachs0_.coach_id as coach_id1_0_1_, coachs0_.career as career2_0_1_, coachs0_.member_id as member_i4_0_1_, coachs0_.name as name3_0_1_, coachs0_.team_id as team_id5_0_1_ from coach coachs0_ where coachs0_.member_id=?
select coachs0_.member_id as member_i4_0_0_, coachs0_.coach_id as coach_id1_0_0_, coachs0_.coach_id as coach_id1_0_1_, coachs0_.career as career2_0_1_, coachs0_.member_id as member_i4_0_1_, coachs0_.name as name3_0_1_, coachs0_.team_id as team_id5_0_1_ from coach coachs0_ where coachs0_.member_id=3;
[2022-01-10 23:27:47.213] [http-nio-9000-exec-2] [4c0222d3] INFO m.p.l.m.c.MemberController - Coach1
I believe it is because you are using the spring-boot default setting which the spring.jpa.open-in-view is set to true .
This property enables OpenSessionInView pattern which you can simply think that a transaction will be opened automatically for you at the very first beginning when processing any HTTP request (e.g. in the Servlet Filter etc). Because of this , a transaction is actually already open before your service method executes and it is still active after your service method completes. Hence you will not experience any LazyInitializationException even after you access non-initialized properties outside the service method as the transaction is still active.
There is a strong debate about whether or not spring-boot should enable it by default in the past . You can refer this for more details if you are interested. I personally would recommend to turn it off.

Spring Boot: How can I reset the entity ID back to 0 every time I make Delete request with client

I have created a server using Java Spring Boot and I made a delete request that deletes all entries in a repository. I tried adding it back and the ID incremented instead of starting back to 0.
#DeleteMapping("/donors")
public String deleteAllDOnors() {
return donorService.deleteAllDonors();
}
#Id
#GeneratedValue
private Long id;
Edit. I tried this so far in a service class:
#PersistenceContext
EntityManager entityManager;
In the same class:
public String deleteAllDonors() {
entityManager
.createNativeQuery("ALTER TABLE SomeTable AUTO_INCREMENT = 1")
.executeUpdate();
donorRepository.deleteAll();
return "All donors removed!";
}
I get a
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:422) ~[hibernate-core-5.4.28.Final.jar:5.4.28.Final]
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1668) ~[hibernate-core-5.4.28.Final.jar:5.4.28.Final]
Not sure what persistence layer you are using but following is an example of JPA on how you can reset it
entityManager
.createNativeQuery("ALTER TABLE SomeTable AUTO_INCREMENT = 1")
.executeUpdate();

Spring Boot JPA EntityListener query causes "don't flush the Session after an exception occurs"

Problem:
I create object A with an EntityListener with #PostPersist-method that will create object B, this works like a charm!
I need to introduce some logic before creating object B, I need to query the database and see if a similar B object already exists in the database. But when I run my query
#Query("select case when count(n) > 0 then true else false end from Notification n where student = :student and initiator = :initiator and entityType = :entityType and entityId = :entityId")
boolean alreadyNotified(#Param("student") Student student, #Param("initiator") Student initiator, #Param("entityType") EntityType entityType, #Param("entityId") Long entityId);
I get the following error:
ERROR org.hibernate.AssertionFailure.<init>(31) - HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in se.hitract.model.Likes entry (don't flush the Session after an exception occurs)
org.hibernate.AssertionFailure: null id in se.hitract.model.Likes entry (don't flush the Session after an exception occurs)
Background:
I have a Spring Boot project with Hibernate and MySql DB and I'm building a simple social media platform where students can upload posts/images and other user can like/comments.
When someone like/comment an object a notification should be sent to the other user. The like object:
#SuppressWarnings("serial")
#Entity
#Table(uniqueConstraints=#UniqueConstraint(columnNames = {"entityType", "entityId", "studentId"}))
#EntityListeners(LikeListener.class)
public class Likes extends CommonEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long likeId;
#NotNull
#Enumerated(EnumType.STRING)
private EntityType entityType;
private Long entityId;
...
}
The LikeListener:
#Component
public class LikeListener {
#PostPersist
public void doThis(Likes like) {
NotificationService notificationService = BeanUtil.getBean(NotificationService.class);
if(like.getEntityType().equals(EntityType.INSPIRATION)) {
InspirationService inspirationService = BeanUtil.getBean(InspirationService.class);
Inspiration inspiration = inspirationService.get(like.getEntityId());
notificationService.createLikeNotification(inspiration.getStudent(), like.getStudent(), EntityType.INSPIRATION, inspiration.getId());
}
if(like.getEntityType().equals(EntityType.COMMENT)) {
CommentService commentService = BeanUtil.getBean(CommentService.class);
Comment comment = commentService.get(like.getEntityId());
notificationService.createLikeNotification(comment.getStudent(), like.getStudent(), EntityType.COMMENT, comment.getId());
}
}
}
and the problem:
public Notification createLikeNotification(Student student, Student initiator, EntityType entityType, Long entityId) {
if(student.equals(initiator) || alreadyNotified(student, initiator, entityType, entityId)) {
return null;
}
Notification notification = createNotification(student,
initiator,
NOTIFICATION_TYPE.LIKE,
entityType,
entityId,
null);
return repository.save(notification);
}
public boolean alreadyNotified(Student student, Student initiator, EntityType entityType, Long entityId) {
return repository.alreadyNotified(student, initiator, entityType, entityId);
}
If I remove the alreadyNotified-call no error is thrown. What am I missing?
It seems that Hibernate flushes the Likes-save before my query is run but then it fails. Do I need to do some manual flush/refresh? I think Hibernate should solve this for me.

Transaction does not retrieve on time data

I have a project with many services. I'm using pure jpa with jersey in my project.
I use entityManager, entityTransaction and entityManagerFactory in this way: for each service I get an EntityManager from DBManager.getEntityManager() and get and entityTransaction from its entityManager
public class DBManager {
public static EntityManager getEntityManager() {
return Persistence.createEntityManagerFactory("projectDataSource").getEntityManager();
}
In each method that need transaction, first I check transaction != null and transaction is not active
Then I begin the transaction.
I evict cache and after all of this I do my job: in this example I get all active users from database, and my service that returns users list to client.
One of my services is : #getUsersService
public class GetUsersServiceImpl {
private static EntityManager entiyManager = DBManager.getEntityManager();
private static EntityTransaction entityTransaction = entityManager.getTransaction();
public List<User> getUsers()
if (transaction != null && !transaction.isActive())
transaction.begin()
entityManager.getEntityMangerFactory().getCache().evictAll();
return entityManger.createQuery("SELECT u FROM USER u WHERE u.isActive = true").getResultList();
}
This code works but not always!
I have another service that changes the users; this user also works and changes user data in database, but after calling change service when I call getUsers service, it retrieves old database data
Why does this happen?
Is it correct the way I use entityManager and entityTransaction ...

OneToMany Create Fails with InvalidDataAccessApiUsageException

I am fairly new to Hibernate and have been using the manual & online forums, but I am stumped on this issue. I’m using Spring 3.2 with Hibernate 4 & Annotations. I have a parent (PledgeForm) & child (PledgeFormGiftLevel) table that is one-to-many.
Domain/Models:
Parent
#Entity
#Table(name="PLEDGE_FORMS")
#SuppressWarnings("serial")
public class PledgeForm implements Serializable {
static final Logger log = Logger.getLogger(PledgeForm.class);
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="pledge_form_seq")
#SequenceGenerator(name="pledge_form_seq", sequenceName="PLEDGE_FORM_SEQ")
#Column(name="ID", unique=true, nullable=false)
private Integer id;
….
#OneToMany(mappedBy="pledgeForm", fetch=FetchType.EAGER, cascade=CascadeType.ALL)//********1
private List<PledgeFormGiftLevel> pledgeFormGiftLevels = new ArrayList<PledgeFormGiftLevel>();
….
public List<PledgeFormGiftLevel> getPledgeFormGiftLevels() {
return this.pledgeFormGiftLevels;
}
public void setPledgeFormGiftLevels(List<PledgeFormGiftLevel> pledgeFormGiftLevels) {
this.pledgeFormGiftLevels = pledgeFormGiftLevels;
}
//I do not think the following method is needed, but I decided to try it just in case
public void addPledgeFormGiftLevels(PledgeFormGiftLevel pledgeFormGiftLevels) {
pledgeFormGiftLevels.setPledgeForm(this);
getPledgeFormGiftLevels().add(pledgeFormGiftLevels);
}
Child
#Entity
#Table(name="PLEDGE_FORM_GIFT_LEVELS")
#SequenceGenerator(name="pledge_form_gift_level_seq", sequenceName="PLEDGE_FORM_GIFT_LEVEL_SEQ")
#SuppressWarnings("serial")
public class PledgeFormGiftLevel implements Serializable {
static final Logger log = Logger.getLogger(PledgeFormGiftLevel.class);
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="pledge_form_gift_level_seq")
#Column(name="ID", unique=true, nullable=false)
private Integer id;
…
#ManyToOne(fetch=FetchType.EAGER)//yes?
#JoinColumn(name="PLEDGE_FORM_ID", referencedColumnName="ID", insertable=true, updatable=true)//yes?
private PledgeForm pledgeForm = new PledgeForm();
…
public PledgeForm getPledgeForm() {
return pledgeForm;
}
public void setPledgeForm(PledgeForm pledgeForm) {
this.pledgeForm = pledgeForm;
}
Controller (there is a graphic, so I have code to pull in the file):
#Controller
#SessionAttributes("pledgeForm")
public class PledgeFormController {
#Autowired
org.unctv.service.PledgeFormManager Service;
…
#RequestMapping(value = "/saveJdbcPledgeForm", method = RequestMethod.POST, params="save")
public ModelAndView save(
#ModelAttribute("pledgeForm")
#Valid PledgeForm pledgeForm, BindingResult result,
#RequestParam("logoImg") MultipartFile file,
#RequestParam(value="removeLogoImg", required=false) String removeLogoImg) throws Exception {
ModelAndView mav = null;
mav = new ModelAndView("pledgeFormSearch");//Name of the JSP
if (removeLogoImg != null) {
pledgeForm.setLogoFilename(null);
pledgeForm.setLogoImg(null);
pledgeForm.setLogoContentType(null);
} else if (file != null && file.getBytes().length > 0) {
pledgeForm.setLogoFilename(file.getOriginalFilename());
pledgeForm.setLogoImg(file.getBytes());
pledgeForm.setLogoContentType(file.getContentType());
}
Service.save(pledgeForm);
mav.addObject("pledgeForm", pledgeForm);//JSP Form's Command Name (pledgeForm);
mav.addObject("cmdName", "pledgeForm");
mav.addObject("actionType", "Save");
return mav;
}
Service:
#Service("simplePledgeFormManager")
#Transactional(readOnly=true)
public class SimplePledgeFormManager implements PledgeFormManager {
#Autowired
private HibernatePledgeFormDao hibernatePledgeFormDao;
…
#Transactional(readOnly=false)
public void save(PledgeForm pledgeForm) throws Exception {
hibernatePledgeFormDao.save(pledgeForm);
}
DAO:
#Repository("PledgeFormDAO")
public class HibernatePledgeFormDao implements PledgeFormDao {
static final Logger log = Logger.getLogger(HibernatePledgeFormDao.class);
#Autowired
private SessionFactory sessionFactory;
...
#Override
public void save(PledgeForm pledgeForm) throws Exception {
sessionFactory.getCurrentSession().saveOrUpdate(pledgeForm);
}
Using the code above, parent/child records can be selected and updated fine. When I display the “trace” messages from hibernate, the update does have this trace message about the child, though:
[2013-12-06 10:31:24,648] TRACE Persistent instance of: org.unctv.domainmodel.PledgeFormGiftLevel
[2013-12-06 10:31:24,649] TRACE Ignoring persistent instance
[2013-12-06 10:31:24,649] TRACE Object already associated with session: [org.unctv.domainmodel.PledgeFormGiftLevel#1]
The create always gives this error if there is a child record:
object references an unsaved transient instance - save the transient instance before flushing: org.unctv.domainmodel.PledgeForm; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.unctv.domainmodel.PledgeForm
When I look at the hibernate logs, I see that it updates the parent & the child based on transient objects. Then it tries to flush & finds a persistent copy of the child, so it rolls back everything.
[2013-12-06 10:34:13,615] TRACE Automatically flushing session
[2013-12-06 10:34:13,615] TRACE Flushing session
[2013-12-06 10:34:13,615] DEBUG Processing flush-time cascades
[2013-12-06 10:34:13,615] TRACE Processing cascade ACTION_SAVE_UPDATE for: org.unctv.domainmodel.PledgeForm
[2013-12-06 10:34:13,615] TRACE Cascade ACTION_SAVE_UPDATE for collection: org.unctv.domainmodel.PledgeForm.pledgeFormGiftLevels
[2013-12-06 10:34:13,615] TRACE Cascading to save or update: org.unctv.domainmodel.PledgeFormGiftLevel
[2013-12-06 10:34:13,616] TRACE Persistent instance of: org.unctv.domainmodel.PledgeFormGiftLevel
[2013-12-06 10:34:13,616] TRACE Ignoring persistent instance
[2013-12-06 10:34:13,616] TRACE Object already associated with session: [org.unctv.domainmodel.PledgeFormGiftLevel#51]
[2013-12-06 10:34:13,616] TRACE Done cascade ACTION_SAVE_UPDATE for collection: org.unctv.domainmodel.PledgeForm.pledgeFormGiftLevels
[2013-12-06 10:34:13,616] TRACE Done processing cascade ACTION_SAVE_UPDATE for: org.unctv.domainmodel.PledgeForm
[2013-12-06 10:34:13,617] DEBUG Dirty checking collections
[2013-12-06 10:34:13,617] TRACE Flushing entities and processing referenced collections
[2013-12-06 10:34:13,617] DEBUG Collection found: [org.unctv.domainmodel.PledgeForm.pledgeFormGiftLevels#51], was: [<unreferenced>] (initialized)
[2013-12-06 10:34:13,618] DEBUG rolling back
[2013-12-06 10:34:13,618] DEBUG rolled JDBC Connection
The Hibernate documentation shows this as even simpler than I my code is, but I had to add the fetch & cascade values. I’ve played with changing the fetch & cascade values & placement (starting with the Hibernate documentation & then adding on), but everything else I try still causes the create to fail & often causes the update to fail too.
Many forum posts that I find show flush() or evict(). I am not certain if it is Hibernate 4 or annotations (#Transactional, I think) I’m using, but I do not see a place for that in my code. From the Hibernate trace logs, I can see that flushing is occurring automatically with in the saveOrUpdate() method.
I also tried dropping the tables & sequences & starting fresh.
Any advice about getting the create to work is appreciated. If you can point me to specific documentation that I missed, that is appreciated as well.
Thanks,
Bonnie
I noticed that equals and hashcode have not been overridden in the entities. These methods are used to compare objects to determine their equality. Hibernate may not be able to determine if an existing instance of the entity exists without these methods being overridden. Try providing implementations for hashcode and equals.
If your using Eclipse, press CTRL + SHIFT + S, H to bring up the dialog for creating the hashcode and equals methods. Pick fields that contain values that are relatively unchanged and then generate the methods.
Also be sure that you are managing both sides of the entity as discussed in the above comments:
public ModelAndView save(
#ModelAttribute("pledgeForm")
#Valid PledgeForm pledgeForm, BindingResult result,
#RequestParam("logoImg") MultipartFile file,
#RequestParam(value="removeLogoImg", required=false) String removeLogoImg) throws Exception {
ModelAndView mav = null;
mav = new ModelAndView("pledgeFormSearch");//Name of the JSP
//Manage both sides of the entity
List<PledgeFormGiftLevel> levels = pledgeForm.getPledgeFormGiftLevels();
for(PledgeFormGiftLevel level: levels){
level.setPledgeForm(pledgeForm);
}
if (removeLogoImg != null) {
pledgeForm.setLogoFilename(null);
pledgeForm.setLogoImg(null);
pledgeForm.setLogoContentType(null);
} else if (file != null && file.getBytes().length > 0) {
pledgeForm.setLogoFilename(file.getOriginalFilename());
pledgeForm.setLogoImg(file.getBytes());
pledgeForm.setLogoContentType(file.getContentType());
}
Service.save(pledgeForm);
mav.addObject("pledgeForm", pledgeForm);//JSP Form's Command Name (pledgeForm);
mav.addObject("cmdName", "pledgeForm");
mav.addObject("actionType", "Save");
return mav;
}

Resources