mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1457 - Issue with @ManyToOne with @EmbeddedId where only some columns are insertable or updatable
This commit is contained in:
@@ -357,14 +357,34 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
* Return true if this association is updateable.
|
||||
*/
|
||||
public boolean isUpdateable() {
|
||||
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isUpdateable();
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
if (columns.length <= 0) {
|
||||
return true;
|
||||
}
|
||||
for (TableJoinColumn column : columns) {
|
||||
if (column.isUpdateable()) {
|
||||
// at least 1 is updatable
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this association is insertable.
|
||||
*/
|
||||
public boolean isInsertable() {
|
||||
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isInsertable();
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
if (columns.length <= 0) {
|
||||
return true;
|
||||
}
|
||||
for (TableJoinColumn column : columns) {
|
||||
if (column.isInsertable()) {
|
||||
// at least 1 is insertable
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,16 +463,18 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
String matchColumn = col.getForeignDbColumn();
|
||||
String localColumn = col.getLocalDbColumn();
|
||||
String localSqlFormula = col.getLocalSqlFormula();
|
||||
boolean insertable = col.isInsertable();
|
||||
boolean updateable = col.isUpdateable();
|
||||
|
||||
for (int j = 0; j < props.length; j++) {
|
||||
if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
|
||||
return new ImportedIdSimple(owner, localColumn, localSqlFormula, props[j], j);
|
||||
return new ImportedIdSimple(owner, localColumn, localSqlFormula, props[j], j, insertable, updateable);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < others.length; j++) {
|
||||
if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
|
||||
return new ImportedIdSimple(owner, localColumn, localSqlFormula, others[j], j + props.length);
|
||||
return new ImportedIdSimple(owner, localColumn, localSqlFormula, others[j], j + props.length, insertable, updateable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,11 @@ public class ImportedIdEmbedded implements ImportedId {
|
||||
@Override
|
||||
public void dmlAppend(GenerateDmlRequest request) {
|
||||
|
||||
boolean update = request.isUpdate();
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
request.appendColumn(anImported.localDbColumn);
|
||||
if (anImported.isInclude(update)) {
|
||||
request.appendColumn(anImported.localDbColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,30 +103,28 @@ public class ImportedIdEmbedded implements ImportedId {
|
||||
@Override
|
||||
public Object bind(BindableRequest request, EntityBean bean) throws SQLException {
|
||||
|
||||
Object embeddedId = null;
|
||||
|
||||
if (bean != null) {
|
||||
embeddedId = foreignAssocOne.getValue(bean);
|
||||
}
|
||||
Object embeddedId = (bean == null) ? null : foreignAssocOne.getValue(bean);
|
||||
|
||||
boolean update = request.isUpdate();
|
||||
if (embeddedId == null) {
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
if (anImported.owner.isUpdateable()) {
|
||||
if (anImported.isInclude(update)) {
|
||||
request.bind(null, anImported.foreignProperty);
|
||||
}
|
||||
}
|
||||
// return anything non-null to skip a derived relationship update
|
||||
return Object.class;
|
||||
|
||||
} else {
|
||||
EntityBean embedded = (EntityBean) embeddedId;
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
if (anImported.owner.isUpdateable()) {
|
||||
if (anImported.isInclude(update)) {
|
||||
Object scalarValue = anImported.foreignProperty.getValue(embedded);
|
||||
request.bind(scalarValue, anImported.foreignProperty);
|
||||
}
|
||||
}
|
||||
return embedded;
|
||||
}
|
||||
// hmmm, not worrying about this just yet
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,15 +47,32 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
|
||||
protected final int position;
|
||||
|
||||
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position) {
|
||||
/**
|
||||
* If true include in insert.
|
||||
*/
|
||||
private final boolean insertable;
|
||||
|
||||
/**
|
||||
* If true include in update.
|
||||
*/
|
||||
private final boolean updateable;
|
||||
|
||||
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position,
|
||||
boolean insertable, boolean updateable) {
|
||||
this.owner = owner;
|
||||
this.localDbColumn = InternString.intern(localDbColumn);
|
||||
this.localSqlFormula = InternString.intern(localSqlFormula);
|
||||
this.foreignProperty = foreignProperty;
|
||||
this.position = position;
|
||||
this.insertable = insertable;
|
||||
this.updateable = updateable;
|
||||
this.logicalName = InternString.intern(owner.getName() + "." + foreignProperty.getName());
|
||||
}
|
||||
|
||||
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position) {
|
||||
this(owner, localDbColumn, localSqlFormula, foreignProperty, position, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list as an array sorted into the same order as the Bean Properties.
|
||||
*/
|
||||
@@ -68,6 +85,13 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
return importedIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if it should be included in the update (or insert).
|
||||
*/
|
||||
public boolean isInclude(boolean update) {
|
||||
return (update) ? updateable : insertable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// remove FindBugs warning
|
||||
|
||||
@@ -19,6 +19,11 @@ public class DeleteHandler extends DmlHandler {
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and bind the delete statement.
|
||||
*/
|
||||
|
||||
@@ -29,10 +29,6 @@ public class GenerateDmlRequest {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void appendColumnIsNull(String column) {
|
||||
appendColumn(column, IS_NULL);
|
||||
}
|
||||
|
||||
public void appendColumn(String column) {
|
||||
//String bind = (insertMode > 0) ? "?" : "=?";
|
||||
appendColumn(column, "?");
|
||||
@@ -91,4 +87,7 @@ public class GenerateDmlRequest {
|
||||
this.prefix2 = ", ";
|
||||
}
|
||||
|
||||
public boolean isUpdate() {
|
||||
return insertMode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,11 @@ public class InsertHandler extends DmlHandler {
|
||||
this.concatinatedKey = meta.isConcatenatedKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and bind the insert statement.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,11 @@ public class UpdateHandler extends DmlHandler {
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and bind the update statement.
|
||||
*/
|
||||
|
||||
@@ -59,4 +59,8 @@ public interface BindableRequest {
|
||||
*/
|
||||
long now();
|
||||
|
||||
/**
|
||||
* Return true if this is an update request.
|
||||
*/
|
||||
boolean isUpdate();
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ public class FactoryAssocOnes {
|
||||
*/
|
||||
public void create(List<Bindable> list, BeanDescriptor<?> desc, DmlMode mode) {
|
||||
|
||||
BeanPropertyAssocOne<?>[] ones = desc.propertiesOneImported();
|
||||
|
||||
for (BeanPropertyAssocOne<?> one : ones) {
|
||||
for (BeanPropertyAssocOne<?> one : desc.propertiesOneImported()) {
|
||||
if (!one.isImportedPrimaryKey()) {
|
||||
switch (mode) {
|
||||
case INSERT:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.tests.model.composite;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class CkeClient {
|
||||
|
||||
@EmbeddedId
|
||||
private CkeClientKey clientPK;
|
||||
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = "username", referencedColumnName = "username"),
|
||||
@JoinColumn(name = "cod_cpny", referencedColumnName = "cod_cpny", insertable = false, updatable = false)
|
||||
})
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
private CkeUser user;
|
||||
|
||||
private String notes;
|
||||
|
||||
public CkeUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(CkeUser user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public CkeClientKey getClientPK() {
|
||||
return clientPK;
|
||||
}
|
||||
|
||||
public void setClientPK(CkeClientKey clientPK) {
|
||||
this.clientPK = clientPK;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(String notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.tests.model.composite;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class CkeClientKey {
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(name = "cod_cpny")
|
||||
private int codCompany;
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(name = "cod_client")
|
||||
private String codClient;
|
||||
|
||||
public CkeClientKey(int codCompany, String codClient) {
|
||||
this.codCompany = codCompany;
|
||||
this.codClient = codClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CkeClientKey that = (CkeClientKey) o;
|
||||
return codCompany == that.codCompany &&
|
||||
Objects.equals(codClient, that.codClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(codCompany, codClient);
|
||||
}
|
||||
|
||||
public int getCodCompany() {
|
||||
return codCompany;
|
||||
}
|
||||
|
||||
public void setCodCompany(int codCompany) {
|
||||
this.codCompany = codCompany;
|
||||
}
|
||||
|
||||
public String getCodClient() {
|
||||
return codClient;
|
||||
}
|
||||
|
||||
public void setCodClient(String codClient) {
|
||||
this.codClient = codClient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.tests.model.composite;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
public class CkeUser {
|
||||
|
||||
@EmbeddedId
|
||||
private CkeUserKey userPK;
|
||||
|
||||
private String name;
|
||||
|
||||
public CkeUserKey getUserPK() {
|
||||
return userPK;
|
||||
}
|
||||
|
||||
public void setUserPK(CkeUserKey userPK) {
|
||||
this.userPK = userPK;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.tests.model.composite;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class CkeUserKey {
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(name = "cod_cpny")
|
||||
private int codCompany;
|
||||
|
||||
@Basic(optional = false)
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
public CkeUserKey(int codCompany, String username) {
|
||||
this.codCompany = codCompany;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CkeUserKey that = (CkeUserKey) o;
|
||||
return codCompany == that.codCompany &&
|
||||
Objects.equals(username, that.username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(codCompany, username);
|
||||
}
|
||||
|
||||
public int getCodCompany() {
|
||||
return codCompany;
|
||||
}
|
||||
|
||||
public void setCodCompany(int codCompany) {
|
||||
this.codCompany = codCompany;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.tests.model.composite;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestCompositeKeyUserClient extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
CkeUser user0 = new CkeUser();
|
||||
user0.setUserPK(new CkeUserKey(20, "sally"));
|
||||
user0.setName("sally");
|
||||
Ebean.save(user0);
|
||||
|
||||
CkeUser user1 = new CkeUser();
|
||||
user1.setUserPK(new CkeUserKey(20, "frank"));
|
||||
user1.setName("hello");
|
||||
Ebean.save(user1);
|
||||
|
||||
CkeClient client = new CkeClient();
|
||||
client.setNotes("try it");
|
||||
client.setClientPK(new CkeClientKey(20, "susan"));
|
||||
client.setUser(user1);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.save(client);
|
||||
|
||||
client.setNotes("update it");
|
||||
client.setUser(user0);
|
||||
|
||||
Ebean.save(client);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into cke_client (cod_cpny, cod_client, notes, username) values (?,?,?,?)");
|
||||
assertThat(sql.get(1)).contains("update cke_client set notes=?, username=? where cod_cpny=? and cod_client=?");
|
||||
|
||||
Ebean.delete(client);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user