I implemented a manyToMany relationship in Symfony2/Doctrine, where the relation is an Entity itself (that was necessary because I needed to add additional columns - notice that these additional columns are not mentioned below).
There are a couple of Q&As around here that recommend to take the relationship as an entity (e.g. here, here, here or here).
I have automatically generated all getter and setter methods via doctrine:generate:entities (this explains why it is called addCompanie instead of addCompany), so I would have thought that this covers my oneToMany/ManyToOne relationship.
And creating a User and a Company work fine so far. But as soon as I try to assign a user to a company I get the following error message:
Found entity of type Acme\MyBundle\Entity\User on
association Acme\MyBundle\Entity\Company#employees,
but expecting Acme\MyBundle\Entity\CompanyHasUser
These are my Doctrine definitions (yml):
# User
Acme\MyBundle\Entity\User:
type: entity
fields:
id:
id: true
type: integer
generator:
strategy: AUTO
oneToMany:
companies:
targetEntity: Acme\MyBundle\Entity\CompanyHasUser
mappedBy: employees
# Company
Acme\MyBundle\Entity\Company:
type: entity
fields:
id:
id: true
type: integer
generator:
strategy: AUTO
oneToMany:
employees:
targetEntity: Acme\MyBundle\Entity\CompanyHasUser
mappedBy: companies
# CompanyHasUser
Acme\MyBundle\Entity\CompanyHasUser:
type: entity
fields:
id:
id: true
type: integer
generator:
strategy: AUTO
manyToOne:
companies:
targetEntity: Acme\MyBundle\Entity\Company
inversedBy: employees
joinColumns:
company_id:
referencedColumnName: id
nullable: false
employees:
targetEntity: Acme\MyBundle\Entity\User
inversedBy: companies
joinColumns:
user_id:
referencedColumnName: id
nullable: false
This is what my Entity class User look like
namespace Acme\MyBundle\Entity;
class User
{
private $id;
private $companies;
public function __construct()
{
$this->companies = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addCompany(\Acme\MyBundle\Entity\CompanyHasUser $companies)
{
$this->companies[] = $companies;
return $this;
}
public function removeCompany(\Acme\MyBundle\Entity\CompanyHasUser $companies)
{
$this->companies->removeElement($companies);
}
public function getCompanies()
{
return $this->companies;
}
}
This is what my Entity class Company look like
namespace Acme\MyBundle\Entity;
class Company
{
private $id;
private $employees;
public function __construct($name, $companyAdmin)
{
$this->employees = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addEmployee(\Acme\MyBundle\Entity\CompanyHasUser $employees)
{
$this->employees[] = $employees;
return $this;
}
public function removeEmployee(\Acme\MyBundle\Entity\CompanyHasUser $employees)
{
$this->employees->removeElement($employees);
}
public function getEmployees()
{
return $this->employees;
}
}
And this is what my Entity class CompanyHasUser looks like (relation between User and Company, but as far as this relation contains further columns - not mentioned in these code snippets - I had to create it as an Entity):
namespace Acme\MyBundle\Entity;
class CompanyHasUser
{
private $companies;
private $employees;
public function setCompanies(\Acme\MyBundle\Entity\Company $companies)
{
$this->companies = $companies;
return $this;
}
public function getCompanies()
{
return $this->companies;
}
public function setEmployees(\Acme\MyBundle\Entity\User $employees)
{
$this->employees = $employees;
return $this;
}
public function getEmployees()
{
return $this->employees;
}
}
My Controller logic is:
// Create new User (employee)
$user = new User();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
// Create new Company (employer)
$company = new Company();
// Create relationship
$company->addEmployee($user);
$em->persist($company);
// Flush
$em->flush();
I found it out with huge thanks to Bronchas answer here. Controller logic can be implemented as follows:
// Create new User (employee)
$user = new User();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
// Create new Company (employer)
$company = new Company();
$em->persist($company);
// Employ User at Company
$employment = new CompanyHasUser();
$employment->setEmployees($user);
$employment->setCompanies($company);
$em->persist($employment);
// Flush
$em->flush();
Related
Hi I have a table in DynamoDb like this:
{
"partitionKey": ...
"sortKey": ...
"fieldOne": ...
"fieldTwo": ...
"fieldThree": ...
}
I added a global secondary index in my CDK like this:
private createTable(): Table {
const fieldOneGsi: GlobalSecondaryIndexProps = {
indexName: 'fieldOneGsi',
partitionKey:{
name: 'partitionKey',
type: AttributeType.STRING
},
sortKey: {
name: 'fieldOne',
type: AttributeType.STRING
},
projectionType: ProjectionType.INCLUDE,
nonKeyAttributes: ['fieldTwo']
}
table.addGlobalSecondaryIndex(fieldOneGsi);
}
Since my GSI will only return 3 attributes: partition_key, fieldOne and fieldTwo. Can I only map the table schema for these attributes for querying using DynamoDbEnhancedClient? For example, something like in Java:
#DynamoDbBean
public class MyTable {
private String partitionKey;
private String fieldOne;
private String fieldTwo;
#DynamoDbPartitionKey
#DynamoDbSecondaryPartitionKey(indexNames = {"fieldOneGsi"})
#DynamoDbAttribute("partition_key")
public String getPartitionKey(){
return partitionKey;
}
#DynamoDbSecondarySortKey(indexNames = {"fieldOneGsi"})
#DynamoDbAttribute("fieldOne")
public String getFieldOne(){
return fieldOne;
}
#DynamoDbAttribute("fieldTwo")
public String getFieldTwo(){
return fieldTwo;
}
}
This should work with no issue, however you're forgetting one aspect, DynamoDB always propagates the base table keys, no matter what you choose to project. For that reason, be sure to have getters/setters for your base table keys also.
I'm upgrading an inherited Grails 2 app to 3.3.10. We have controller methods that rely on Domain class validation to control logic flow, but we can't get DomainClass validation to work in ControllerUnitTests when running in Grails 3
For example,
Gift and RecipientAddress are DomainClasses and RecipientAddress.hasErrors() is used to validate against the RecipientAddress constraints.
def confirmAddress() {
Gift gift = Gift.get(params.giftId)
if (!gift) {
render(view: "index", model: [invalid: true])
return
}
recipientAddress = recipientAddressService.storeAddressInformation(params, gift)
if (recipientAddress.hasErrors()) {
render(view: "index", model: getAddressErrorModel(gift, recipientAddress))
return;
} else {
return [
recipientAddress : recipientAddress,
gift : gift
]
}
}
In the following test, when I debug the controller method it does everything expected but recipientAddress.hasErrors() always returns true and the test fails.
ie:
#Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController> {
#Shared
#AutoCleanup
SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"],
RecipientAddress, Gift)
def setup() {
controller.recipientAddressService = Mock(RecipientAddressService)
}
void "test RecipientAddress Bad PhoneNumber"() {
given:
RecipientAddress recipientAddress = new RecipientAddress(
phone: '123-456-789'
)
UnitTestDataFactory dataFactory = UnitTestDataFactory.getDataFactory()
Gift gift = dataFactory.getMockGift()
gift.save()
params.giftId = gift.id
when:
recipientAddress.validate()
controller.confirmAddress()
then:
recipientAddress.hasErrors()
recipientAddress.getErrors().getFieldError('phone')
1 * controller.recipientAddressService.storeAddressInformation(params, gift) >> recipientAddress
view == '/giftDetails/index'
}
}
Implementing DataTest ie: ...implements ControllerUnitTest<GiftDetailsController>, DataTest { fixes the DomainClass validation, but breaks the controllers ability to get the saved Gift.
Is there a way get DomainClass validation working in a ControllerUnit test?
Fix
Implemented DataTest with mockDomains and had to remove the custom dataStore.
#Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
// #Shared
// #AutoCleanup
// SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"], RecipientAddress, Gift)
void setupSpec() {
mockDomains RecipientAddress, Gift
}
....
Is there a way get DomainClass validation working in a ControllerUnit
test?
Yes.
You probably want something like this...
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
Class[] getDomainClassesToMock() {
[Gift]
}
// ...
}
I am trying to unit test my service layer as advised by #NKosi Here. I am able to do the integration test successfully by implementing the actual factory implementation without mocking anything but can't do the unit test (by mocking IDbConnection and my SQL connection factory class) as Dapper query executing fails with the error 'Object not set to an instance of an object'.
My IDbConnection factory and its implementation is as follow
public interface IDbConnectionFactory
{
IDbConnection CreateConnection();
}
public class ConnectionSetings
{
public string Name { get; set; }
}
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly ConnectionSetings connectionSettings;
public SqlConnectionFactory(ConnectionSetings connectionSettings)
{
this.connectionSettings = connectionSettings;
}
public IDbConnection CreateConnection()
{
return new SqlConnection(connectionSettings.Name);
}
}
And the XUnit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var connection = new Mock<IDbConnection>();
var dbConnectionFactory = new Mock<IDbConnectionFactory>();
dbConnectionFactory.Setup(x => x.CreateConnection()).Returns(connection.Object);
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactory.Object).Get(1);
//Assert
result.Name.Equals("Test Name 1");
//Assert.Equal("Test Name 1", result.Name);
}
And the Service Layer is as follow
public class SqlRestaurantDataCL : IRestaurantDataCL
{
private readonly IDbConnectionFactory factory;
public SqlRestaurantDataCL(IDbConnectionFactory factory)
{
this.factory = factory;
}
public Restaurant Get(int id)
{
using (var connection = factory.CreateConnection())
{
var selectSql = #"SELECT * From Restaurants Where Id = #Id";
var restaurant = connection.QuerySingleOrDefault<Restaurant>(selectSql, new
{
id
});
return restaurant;
}
}
}
Following is the error screenshot
Following is the answer to my question if anyone is in similar situation. Before following this solution, I would suggest to read #NKosi comments above and consult #Mikhail's solution Here.
ServiceStack.OrmLite.Sqlite package added to use in memory appraoch
internal class InMemoryDatabase
{
private readonly OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
public void Insert<T>(IEnumerable<T> items)
{
using (var db = this.OpenConnection())
{
db.CreateTableIfNotExists<T>();
foreach (var item in items)
{
db.Insert(item);
}
}
}
}
Data Access layer is as follow
public IEnumerable<Restaurant> GetAll()
{
using (var connection = factory.CreateConnection())
{
//return connection.Query<Restaurant>("Select * From [dbo].[Restaurants] Order By Name");
return connection.Query<Restaurant>("Select * From Restaurant Order By Name");
}
}
Unit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var restaurants = new List<Restaurant>
{
new Restaurant { Id = 1, Name = "Test Name 1", Cuisine = CuisineType.None},
new Restaurant { Id = 2, Name = "Test Name 2", Cuisine = CuisineType.French},
new Restaurant { Id = 3, Name = "Test Name 3", Cuisine = CuisineType.German},
new Restaurant { Id = 4, Name = "Test Name 4", Cuisine = CuisineType.Italian},
new Restaurant { Id = 5, Name = "Test Name 5", Cuisine = CuisineType.None}
};
var db = new InMemoryDatabase();
db.Insert(restaurants);
var connection = new Mock<IDbConnection>();
var dbConnectionFactoryMock = new Mock<IDbConnectionFactory>();
dbConnectionFactoryMock.Setup(c => c.CreateConnection()).Returns(db.OpenConnection());
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactoryMock.Object).GetAll();
//Assert
result.Should().BeEquivalentTo(restaurants);
}
I have 2 simple tables.
create table Owner
(
Id int primary key,
Name nvarchar(100),
);
create table Status
(
Id int primary key,
BrandName nvarchar(50)
OwnerId int foreign key references Owner(Id),
);
In app I map these tables to model classes:
public class Owner
{
public int Id {get;set;}
public string Name{get;set;}
public Status Status {get;set;}
}
public class Status
{
public int Id {get;set;}
public string Brand {get;set;}
public int OwnerId {get;set;}
}
I use dapper and dapper extension.
I would like map relation one to one in dapper in classmapper. It’s possible?
My goal is when I added owner object which has set up also property Status to db via repository
it also insert record do status table.
What is best way to achieve this behavior?
public class OwnerMapper : ClassMapper<Owner>
{
public OwnerMapper()
{
Table("Owner");
Map(p=>p.Id).Column("Id").Key(KeyType.Assigned);
Map(p=>p.Name).Column("Name");
//how map property status
}
}
public class StatusMapper : ClassMapper<Status>
{
public StatusMapper()
{
Table("Status");
Map(p=>p.Id).Column("Id").Key(KeyType.Identity);
Map(p=>p.Brand).Column("BrandName");
Map(p=>OwnerId).Column("OwnerId");
}
}
You can try Dapper's multi-mapping feature:
[Test]
public void MyTest()
{
var connection = new SqlConnection("conn string here");
connection.Open();
const string sql =
"select Id = 1, Name ='Bill Gates', Id = 1, Brand = 'Apple', OwnerId = 1";
var result = connection.Query<Owner, Status, Owner>(sql,
(owner, status) =>
{
owner.Status = status;
return owner;
},
commandType: CommandType.Text
).FirstOrDefault();
Assert.That(result, Is.Not.Null);
Assert.That(result.Status, Is.Not.Null);
Assert.That(result.Status.Brand, Is.EqualTo("Apple"));
connection.Close();
}
I am trying my way around JPA but I cant get this to work the way I understand them.
Its the onetoone bidirectional mapping between an Order and an OrderInvoice class which is a required
association
My entities are marked as this
#Entity
#Table(name = "Orders")
public class Order {
#Id
#GeneratedValue
#Column(name = "ORDER_ID")
private int orderId;
#OneToOne(optional=false,cascade=CascadeType.ALL, mappedBy="order", targetEntity=OrderInvoice.class)
private OrderInvoice invoice;
}
#Entity
#Table(name = "ORDER_INVOICE")
public class OrderInvoice {
#Id
#GeneratedValue
#Column(name = "INVOICE_ID", nullable = false)
private int invoiceId;
#OneToOne(optional = false)
#JoinColumn(name="ORDER_ID")
private Order order;
}
My test class is like this.
#Test
public void createOrder() {
Order order = createOrderImpl();
assertNotNull(order);
}
private Order createOrderImpl() {
OrderInvoice orderInvoice = new OrderInvoice(new Date(), 100.0, null,
null, new Date());
Order order = new Order(100.0, "JOHN Doe's Order", new Date(), new Date(),orderInvoice);
orderDao.create(order);
return order;
}
But I am encountering below problem when I run my Test
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: order.OrderInvoice.order
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: order.OrderInvoice.order
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
try to
orderInvoice.setOrder(order);
orderDao.create(order);