Reputation: 121
I try to persist many to one relation through SpringMVC and JPA.
My view is the following:
<form:form modelAttribute="wordpair" action="addWordPair" method="post">
<label for="semanticUnitOne">Enter word one: </label>
<form:input path="semanticUnitOne"/>
<br/>
<label for="semanticUnitTwo">Enter word two: </label>
<form:input path="semanticUnitTwo"/>
<br/>
<div class="form-group">
<label for="dictionary">Dictionary</label>
<form:select path="dictionary" cssClass="selectpicker" items="${dictionaries}" itemLabel="name" itemValue="id">
<%--<form:options items="${dictionaries}" itemValue="id" itemLabel="name"/>--%>
</form:select>
</div>
<input type="submit" class="btn" value="Add word pair"/>
</form:form>
<div class="control-group">
</div>
</div>
Controller is the following:
@Controller
public class WordPairController {
@Autowired
WordPairService wordPairService;
@Autowired
DictionaryService dictionaryService;
@RequestMapping(value = "createWordPair", method = RequestMethod.GET)
public String createWordPair(Model model) {
model.addAttribute("wordpair", new WordPair());
model.addAttribute("dictionaries", this.dictionaryService.findAll());
return "addWordPair";
}
@RequestMapping(value = "addWordPair", method = RequestMethod.POST)
public String addWordPair(@ModelAttribute("wordpair") WordPair wordPair, BindingResult result, Dictionary dictionary) {
wordPair.setDictionary(dictionary);
wordPairService.save(wordPair);
return "addWordPair";
}
@RequestMapping(value = "wordPairGet", method = RequestMethod.GET)
public String wordPairGet(Model model) {
model.addAttribute("wordPair", wordPairService.findAllWordPairs());
return "wordPairGet";
}
}
Entitiy for dictionary is the following:
@Entity
public class Dictionary {
@Id
@GeneratedValue
private Long id;
private String name;
private String comment;
@OneToMany(mappedBy = "dictionary", cascade = CascadeType.All, fetch=FetchType.EAGER)
private List<WordPair> wordPairs;
and wordpair entity:
@Entity
@Table
public class WordPair {
@Id
@GeneratedValue
private Long id;
private String semanticUnitOne;
private String semanticUnitTwo;
private int rating;
@ManyToOne(fetch = FetchType.EAGER, cascade = javax.persistence.CascadeType.All)
private Dictionary dictionary;
Also there is a service and DAO,
@Repository("dictionaryDao")
public class DictionaryDaoImpl implements DictionaryDao {
@PersistenceContext
private EntityManager em;
public Dictionary save(Dictionary dictionary) {
em.persist(dictionary);
em.flush();
return dictionary;
}
public List<Dictionary> findAllDictionaries() {
List<Dictionary> result = em.createQuery("SELECT d FROM Dictionary d", Dictionary.class).getResultList();
return result;
}
}
@Repository("wordPairRepository")
public class WordPairDaoImpl implements WordPairDao {
@PersistenceContext
private EntityManager em;
public List<WordPair> findAllWordPairs() {
List<WordPair> result = em.createQuery("SELECT p FROM WordPair p", WordPair.class).getResultList();
return result;
}
public WordPair save(WordPair wordPair) {
em.persist(wordPair);
em.flush();
return wordPair;
}
The problem is when I try to persist WordPair
and link Dictionary
entity to it, hibernate persists new Dictionary entity and then persists new WordPair entity with attaching new dictionary to it. How to attach existing dictionary entity, which I have chosen with form:select item in view?
If I change
@ManyToOne(fetch = FetchType.EAGER, cascade = javax.persistence.CascadeType.All)
option in WordPair to Merge or something, it gives
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing:
Upvotes: 4
Views: 4484
Reputation: 7950
I am using Spring JPA with Hibernate 5, I let hibernate create the tables.
I have entities:
@Entity(name = "DICTIONARY")
public class Dictionary {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String comment;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="DICTIONARY_ID")
private List<WordPair> wordPairs;
and
@Entity(name = "WORDPAIR")
public class WordPair {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String semanticUnitOne;
private String semanticUnitTwo;
private int rating;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Dictionary dictionary;
I have JPA Repostories, note the @Transaction is important
@Repository
@Transactional
public interface DictionaryRepository extends CrudRepository<Dictionary, Long> {
}
and
@Repository
@Transactional
public interface WordPairRepository extends CrudRepository<WordPair, Long> {
}
The tables created look like this :
DICTIONARY
ID
COMMENT
NAME
WORDPAIR
ID
RATING
SEMANTICUNITONE
SEMANTICUNITTWO
DICTIONARY_ID
The following tests shows I can do updates in both directions.
Dictionary dictionary = makeDictionary("comment", "dictionary 1");
dictionary.setWordPairs(new ArrayList<WordPair>());
dictionary.getWordPairs().add(makeWordPair("George", "Unit 3", "Unit 4", 65));
dictionary.getWordPairs().add(makeWordPair("Greg", "Unit 2", "Unit 1", 25));
dictionaryRepository.save(dictionary);
dictionary = dictionaryRepository.findOne(1l);
assertEquals("dictionary 1", dictionary.getName());
WordPair wordPair = wordPairRepository.findOne(dictionary.getWordPairs().get(0).getId());
assertEquals("dictionary 1", wordPair.getDictionary().getName());
wordPair.getDictionary().setName("dictionary 2");
wordPairRepository.save(wordPair);
wordPair = wordPairRepository.findOne(dictionary.getWordPairs().get(0).getId());
assertEquals("dictionary 2", wordPair.getDictionary().getName());
dictionary = dictionaryRepository.findOne(1l);
assertEquals("dictionary 2", dictionary.getName());
Upvotes: 2
Reputation: 3883
At first, you can remove @ManyToOne
from wordPairs field, you just need to define the relationship in Dictory. And add @JoinColumn(name = "dict_id")
to tell that the column in wordPair
table for reference.
If it can not fix your problem, you should check the wordPairs
data of dictory, whether the id
field has value.
Check the code:
@Entity
public class Dictionary {
@Id
@GeneratedValue
private Long id;
private String name;
private String comment;
@OneToMany(mappedBy = "dictionary", cascade = CascadeType.All, fetch=FetchType.EAGER)
@JoinColumn(name = "dict_id")// the column name in WorPair table to refer Dictory.
private List<WordPair> wordPairs;
Upvotes: -1