@ManyToOne ConstraintViolationException

markai

Aktives Mitglied
Ich würde gerne in meinen Blog Posts speichern können. Dazu habe ich folgende Entities.

Meinen Blog:

Java:
@Entity
@Table(name = "Blog")
public class Blog implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    @NotBlank
    @Column(nullable = false, unique = true)
    private String name;
    
    @Lob
    @Column(columnDefinition = "CLOB")
    private String description;
    
    @OneToMany(mappedBy = "blog")
    private List<Post> posts = new ArrayList<Post>();

und meine Posts:
Java:
@Entity
@Table(name = "Post")
public class Post implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    //...

    @ManyToOne @JoinColumn(name = "BLOG_ID", nullable = false)
    private Blog blog;

Nun habe ich folgenden Test geschrieben:
Java:
    @Test
    public void testSavePost() {
        Blog blog1 = testBlogs.get(0);
        Post post = new Post("Title", "Test Content", blog1);
        login("user1@test.com", "topsecret1");
        postService.save(post);

Mein Problem ist, dass mein postService den Post nicht persistieren kann wenn ich einen Blog mit übergebe. In der Exception steht was von "integrity constraint violation: foreign key no action; ... table: POST". Leider kenn ich mich nicht so gut aus dass ich diese deuten könnte ;(

Fürs ORM verwende ich Hibernate.

Hier die ganze Output:

Java:
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
	at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)
	at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
	at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:1397)
	at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:108)
	at at.company.aim.blog.services.AbstractTestBase.clearDatabase(AbstractTestBase.java:66)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:33)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
	at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
	at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
	at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:74)
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:136)
	at org.hibernate.hql.internal.ast.exec.BasicExecutor.execute(BasicExecutor.java:103)
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.executeUpdate(QueryTranslatorImpl.java:414)
	at org.hibernate.engine.query.spi.HQLQueryPlan.performExecuteUpdate(HQLQueryPlan.java:282)
	at org.hibernate.internal.SessionImpl.executeUpdate(SessionImpl.java:1290)
	at org.hibernate.internal.QueryImpl.executeUpdate(QueryImpl.java:116)
	at org.hibernate.ejb.QueryImpl.internalExecuteUpdate(QueryImpl.java:196)
	at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:99)
	... 34 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no action; FK_KTXW0U65IC5COCUEQWQ1F6A69 table: POST
	at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
	at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
	at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source)
	at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source)
	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:133)
	... 41 more
Caused by: org.hsqldb.HsqlException: integrity constraint violation: foreign key no action; FK_KTXW0U65IC5COCUEQWQ1F6A69 table: POST
	at org.hsqldb.error.Error.error(Unknown Source)
	at org.hsqldb.StatementDML.performReferentialActions(Unknown Source)
	at org.hsqldb.StatementDML.delete(Unknown Source)
	at org.hsqldb.StatementDML.executeDeleteStatement(Unknown Source)
	at org.hsqldb.StatementDML.getResult(Unknown Source)
	at org.hsqldb.StatementDMQL.execute(Unknown Source)
	at org.hsqldb.Session.executeCompiledStatement(Unknown Source)
	at org.hsqldb.Session.execute(Unknown Source)
	... 44 more
 

redJava99

Bekanntes Mitglied
Beim Versuch, den Post zu speichern, ist der darin referenzierte blog noch nicht persistiert.
Wenn nun versucht wird, den Post zu speichern, muss es aber eine Referenz (den ForeignKey) auf den Blog geben. D.h. du kannst den Post nicht speichern, ohne den Blog vorher zu speichern.
Dafür gibt es 2 Möglichkeiten:
1.: Du speicherst den Blog vorher manuell und übergibst dann das persistierte Objekt an den Post
oder 2.: Du ergänzt die Annotation
Code:
@ManyToOne
mit
Code:
(cascade = CascadeType.ALL)
. Die Annotation veranlasst Hibernate dazu, den Blog zu persistieren, sobald der Post persistiert wird (so, wie du das intuitiv wahrscheinlich angenommen hattest).
 

markai

Aktives Mitglied
Danke für die Antwort! Der Blog ist persistent (Sorry, konntest du nicht wissen).

Java:
    @Before
    public void setUp() {
        testUsers = createTestUsers(5);
        testBlogs = createTestBlogs(testUsers);
    }

    protected List<User> createTestUsers(int numberOfUsers) {
        List<User> result = new ArrayList<User>();
        for (int i = 1; i <= numberOfUsers; i++) {
            User user = new User("user" + i + "@test.com", "First" + i, "Last" + i, "User" + i, "topsecret" + i);
            userService.save(user);
            result.add(user);
        }
        return result;
    }
    
        protected List<Blog> createTestBlogs(List<User> users) {
        List<Blog> blogs = new ArrayList<Blog>(users.size());
        int i = 1;
        for(User user: users) {
            login(user.getEmail(), "topsecret"+i);
            Blog blog = new Blog("User"+i+"'s Blog", TEST_CONTENT);
            blogService.save(blog);
            blogs.add(blog);
            i++;
        }
        return blogs;
    }
 

redJava99

Bekanntes Mitglied
Okay, zweiter Versuch:

mit
Java:
protected List<Blog> createTestBlogs(List<User> users) {
//...
 Blog blog = new Blog("User"+i+"'s Blog", TEST_CONTENT);
 blogService.save(blog);
//...
}
kommt der Blog zwar in die Datenbank, aber in deine Liste steckst du danach das "alte" Objekt, nicht das Objekt aus der Datenbank ("detached entity").

Probiere es mit
Java:
protected List<Blog> createTestBlogs(List<User> users) {
//...
 Blog blog = new Blog("User"+i+"'s Blog", TEST_CONTENT);
 blog = blogService.save(blog);
//...
}

wobei die Save Methode im BlogService etwa so aussieht:
Java:
public Blog save(Blog saveMe) {
//...
 Blog persistedBlog= myEntiyManager.merge(saveMe);
 myEntiyManager.getTransaction().commit();
 return persistedBlog;
//...
}
 

markai

Aktives Mitglied
Hmm dann krieg ich genau den selben Fehler...

Java:
    @Test
    public void testSavePost() {
        User u = new User("user1@test.com", "firstname", "lastname", "nickname", "topsecret1");
        userService.save(u);
        Blog blog1 = new Blog("name", "description");
        login("user1@test.com", "topsecret1");
        blogService.save(blog1);
        Post post = new Post("Title", "Test Content", blog1);
        postService.save(post);
    }

meine save-Methode im Blog sieht so aus.

Java:
@Transactional
@Named
public interface BlogService extends Repository<Blog, Long>{

    @PostAuthorize("hasRole('ROLE_ADMINISTRATOR') or returnObject.owner.email == authentication.name")   
    public Blog save(Blog blog);

Stimmen meine Annotations im Entity?
 

Ähnliche Java Themen

Neue Themen


Oben