Doctrine migrations: create code for mysql and postgresql - doctrine

I am using Doctrine ORM 2.6.1 in a Symfony 3.4.4 project.
Some of my instances work on a MySQL database, some on Postgresql, and a few installations even access a MicosoftSQL server. This works fine without any special changes to my project or entities, I only have to configure the corresponding connection parameters.
But: if I create migrations, only statements compatible with the current database connection are created in the migration file.
I develop with a postgres-conncection, so I only produce postgresql-statements, like:
class Version20180430083616 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('DELETE FROM document_category');
$this->addSql('DROP SEQUENCE document_category_id_seq CASCADE');
$this->addSql('DROP TABLE document_category');
}
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
//...
}
}
My Question: How can I tell the migrations bundle to create statements for each platform, like:
class Version20180430083616 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
if($this->connection->getDatabasePlatform()->getName() == 'postgresql'){
$this->addSql('DELETE FROM document');
$this->addSql('DELETE FROM document_category');
$this->addSql('DROP SEQUENCE document_category_id_seq CASCADE');
$this->addSql('DROP TABLE document_category');
} else if($this->connection->getDatabasePlatform()->getName() == 'mysql'){
...
} else if ($this->connection->getDatabasePlatform()->getName() == 'mssql') { // MicrosoftSQL ?
...
}
}
}
Edit:
So, I think the only solution to my problem is to define multiple database connections and entity managers, and to always create a distinct migration for each connection type. According to this article, I can define several connections as:

I found a doable solution:
inf config.yml I define one connection and one EntityManager per database type:
doctrine:
dbal:
default_connection: pgdb
connections:
pgdb:
driver: pdo_pgsql
host: db
port: 5432
name: pgdb
user: postgres
password: example
charset: utf8
mapping_types:
enum: string
mysql:
driver: pdo_mysql
host: mysqlhost
port: 3306
name: mydb
dbname: mydb
user: root
password: xxx
charset: utf8mb4
default_table_options:
collate: utf8mb4_unicode_ci
mapping_types:
enum: string
mssql:
driver: pdo_sqlsrv
host: mssqlhost
port: 1433
name: msdb
dbname: testdb
user: sa
password: xxx
charset: utf8
mapping_types:
enum: string
orm:
auto_generate_proxy_classes: false
proxy_dir: '%kernel.cache_dir%/doctrine/orm/Proxies'
proxy_namespace: Proxies
entity_managers:
default:
connection: pgdb
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
AppBundle: ~
my:
connection: mydb
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
AppBundle: ~
ms:
connection: msdb
naming_strategy: doctrine.orm.naming_strategy.underscore
mappings:
AppBundle: ~
Then, I can issue the diff-command 3 times instead of only once
$ bin/console doctrine:migrations:diff --em=default
$ bin/console doctrine:migrations:diff --em=my
$ bin/console doctrine:migrations:diff --em=ms
This creates three migrations each starting with a fence line:
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mssql', 'Migration can only be executed safely on \'mssql\'.');
in which I exchange abortIf by skipIf, such that the migration process is not aborted if the current migration if for a different database type, but just skipped:
$this->skipIf($this->connection->getDatabasePlatform()->getName() !== 'mssql', 'Migration can only be executed safely on \'mssql\'.');
I hope this helps somebody.

Related

How to connect to remote oracle database using typeorm in nestjs?

I was wondering how to connect to remote oracle database from nestjs using typeorm.
I installed typeorm and oracle package using following command.
npm i --save #nestjs/typeorm typeorm oracle
npm install oracledb --save
and then tried configuring in app.module.ts using TypeOrmModule.forRoot but it was not succesfull.
Here are my configuration settings.
TypeOrmModule.forRoot({
type: 'oracle',
host: 'ip of hostname',
port: port number,
username: 'username',
password: 'password',
serviceName: 'servicename',
synchronize: false,
entities: []
})
Can anybody help me out what am I missing? Also would like to know how can I execute the query once this connection is succesfully? If any example that would be helpfull.
Got it.
one missing thing was database name.
Added
database: 'databasename' in above configuration and it worked.
But, still my question is how to use this connection in service to fetch/push the data from/to oracle databse?
If you provide a name in your connection details you should be able to refer to the database connection using that. Otherwise, if no name is provided I believe it assigns it the name 'default'.
Basically these are the steps you should perform to use the database connection: (examples below each)
Create a model - this is how TypeORM knows to create a table.
export class Photo {
id: number
name: string
description: string
filename: string
views: number
isPublished: boolean
}
Create an Entity. - this should match your model, with the appropriate decorators. At minimum you should have the #Entity() decorator before your class definition and #Column() before each field.
import { Entity, Column } from "typeorm"
#Entity()
export class Photo {
#Column()
id: number
#Column()
name: string
#Column()
description: string
#Column()
filename: string
#Column()
views: number
#Column()
isPublished: boolean
}
Create your data source - looks like you have already done this. But I would give it a name field and you will need to pass your entities into the entity array you have.
const AppDataSource = new DataSource({
type: "postgres",
name: "photos",
host: "localhost",
port: 5432,
username: "root",
password: "admin",
database: "test",
entities: [Photo],
synchronize: true,
logging: false,
})
Then you can use repositories to manage data in the database:
const photo = new Photo()
photo.name = "Me and Bears"
photo.description = "I am near polar bears"
photo.filename = "photo-with-bears.jpg"
photo.views = 1
photo.isPublished = true
const photoRepository = AppDataSource.getRepository(Photo)
await photoRepository.save(photo)
console.log("Photo has been saved")
const savedPhotos = await photoRepository.find()
console.log("All photos from the db: ", savedPhotos)
For more details I would spend some time reading through the typeORM website, all the examples I pulled were from there:
https://typeorm.io/

Symfony4: How to toggle entities using dotenv and multiple Entity Manager?

How it is possible through dotenv select different entity path with same name of entities.
Originally, We had application in Symfony 3 for people meetings on events of our organization. Then we decided to offer this application to our partners.
One of the partners asked us to customize the application for them with their data and specifications. We basically created a new instance of Symfony application with copy of database tables (with different prefix), changes in Entities to reflect new db table names, and some translation text changes.
It seems that other partners will follow this trend of customized instances.
Therefore, I am trying to update core application to Symfony 4 and I am trying to use multiple Entity Managers and dotenv to differentiate between partners database tables, as described bellow.
In a nutshell, I am trying to use multiple Entity Manager to switch db table names by prefix.
.env
###> doctrine/doctrine-bundle ###
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# Configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=oci8://user:pass#127.0.0.1:1521/XE
EM_TYPE=OpenAccess
###< doctrine/doctrine-bundle ###
Only in security.yaml working env good
security:
encoders:
App\Entity\%env(EM_TYPE)%\Osoba:
providers:
our_db_provider:
entity:
class: App\Entity\%env(EM_TYPE)%\Osoba
property: username
When i tried get %env(EM_TYPE)% in default_entity_manager, then give error You have requested a non-existent service "doctrine.orm.%env(EM_TYPE)%_entity_manager". doctrine.yaml
parameters:
env(DATABASE_URL): ''
doctrine:
dbal:
default_connection: '%env(EM_TYPE)%'
connections:
MeetingTool:
driver: 'oci8'
charset: UTF8
schema_filter: /^MT_/
url: '%env(resolve:DATABASE_URL)%'
OpenAccess:
driver: 'oci8'
charset: UTF8
schema_filter: /^OA6_/
url: '%env(resolve:DATABASE_URL)%'
orm:
auto_generate_proxy_classes: true
default_entity_manager: '%env(EM_TYPE)%'
entity_managers:
MeetingTool:
connection: MeetingTool
mappings:
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/MeetingTool'
prefix: 'App\Entity\MeetingTool'
alias: App2
OpenAccess:
connection: OpenAccess
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/OpenAccess'
prefix: 'App\Entity\OpenAccess'
alias: OpenAccess
and biggest problem is use, how i targeting right entity here? for example loginController.php
<?php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\OpenAccess\LogPrihlaseni;
//use App\Entity\MeetingTool\LogPrihlaseni;
class LoginController extends AbstractController {
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function logPrihlaseni() {
$log = new LogPrihlaseni();

Doctrine with two Entitymanagers looks at wrong database

I did follow these rules of setting up a second connection and a second entity manager in doctrine. The "default" database is called revee and the "source" database reveesrc.
What works
When I dry-run a migration with this code in the postUp() method:
/** #var EntityManager $em */
$em = $this->container->get('doctrine.orm.entity_manager');
/** #var EntityManager $emSrc */
$emSrc = $this->container->get('doctrine.orm.source_entity_manager');
var_dump($emSrc->getConnection()->getDatabase());
$dates = $emSrc->getRepository('App:Dates')->findAll();
Weirdly, I get the database reveesrc written our correctly! Meaning that the mapping from the connection to the entity manager works just fine.
What doesn't work
However, the next line produces the error.
Base table or view not found: 1146 Table 'revee.dates' doesn't exist"
As dates was defined in the Entity folder attached to the second source entity manager I thought doctrine would know where to look for the table. What do I have to do to map the Entity to the other source database?
doctrine.yaml
parameters:
# Adds a fallback DATABASE_URL if the env var is not set.
# This allows you to run cache:warmup even if your
# environment variables are not available yet.
# You should not need to change this value.
env(DATABASE_URL): ''
doctrine:
dbal:
# configure these for your database server
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
source:
url: '%env(resolve:DATABASE_URL_SOURCE)%'
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
orm:
default_entity_manager: default
auto_generate_proxy_classes: true
entity_managers:
default:
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
connection: default
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
source:
naming_strategy: doctrine.orm.naming_strategy.underscore
connection: source
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/EntitySrc'
prefix: 'App\EntitySrc'
alias: App
src\Entity\Dates.php (first lines)
<?php
namespace App\EntitySrc;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\DateSrcRepository")
*/
class Dates
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
src\Repository\DateSrcRepository.php
<?php
namespace App\Repository;
use App\EntitySrc\Dates;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
class DateSrcRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Dates::class);
}
If anybody experiences the same problem, I fixed it by specifying the table explicetly in the entity, prefixing it with the database. If anyone knows if that is not the correct way of solving it I would be interested.
<?php
namespace App\Entity\Src;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\DateSrcRepository")
* #ORM\Table(name="reveesrc.dates")
*/
class Dates
{

Integrating oracle 11g with Grails and Hibernate

I have created a simple grails 3 application. I am trying to connect it to an Oracle database in the datasource configuration.
When I run
SELECT * FROM V$VERSION
in sql developer, the following data is returned back about my database.
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
my application.yml file looks like this:
dataSources:
dataSource:
pooled: true
dialect: org.hibernate.dialect.Oracle10gDialect
driverClassName: 'oracle.jdbc.OracleDriver'
username: 'superCool'
password: 'password'
url: 'jdbc:oracle:thin:#127.0.0.1:1521:coolio'
dbCreate: ''
my build.gradle file contains these lines for hibernate and oracle dependencies.
dependencies {
(...)
compile "org.grails.plugins:hibernate:4.3.10.5"
(...)
compile "org.hibernate:hibernate-ehcache"
compile("com.oracle:ojdbc7:12.1.0.2")
}
My service file looks as follows:
class DatabaseService {
DataSource dataSource
public void testMyDb(User user) {
try {
registerUser(new Sql(dataSource), user)
} catch (SQLException e) {
LOGGER.error("unable to register the user", e)
throw e
}
}
public void registerUser(Sql sql, User user) {
sql.call("{call isertUser(?)}", [user.name])
}
If I remove the
compile "org.grails.plugins:hibernate:4.3.10.5"
from the build.gradle, I can run my integration tests and the database is successfully reached. If I keep it there, I get the following error:
ERROR DatabaseService - unable to register the user
java.sql.SQLRecoverableException: Closed Connection
at oracle.jdbc.driver.PhysicalConnection.getAutoCommit(PhysicalConnection.java:2254) ~[ojdbc7-12.1.0.2.jar:12.1.0.2.0]
UPDATE 1:
I updated my build.gradle file to reference
compile("com.oracle:ojdbc6:11.2.0.2")
as opposed to
compile("com.oracle:ojdbc7:12.1.0.2")
and the generated error now refers to the setter:
ERROR DatabaseService - unable to register the user
java.sql.SQLRecoverableException: Closed Connection
at oracle.jdbc.driver.PhysicalConnection.setAutoCommit(PhysicalConnection.java:2254) ~[ojdbc7-12.1.0.2.jar:12.1.0.2.0]
UPDATE 2:
I caught the SQLException and got the sql error code from it. The code returned back: 08003. According to https://docs.oracle.com/cd/E15817_01/appdev.111/b31228/appd.htm ,
08003 - connection does not exist
So at this point, I set the pooled flag to false in the datasource, and everything worked just fine. So the problem here is narrowed down to that. The plugin is not reacting well to the pooled properties.
I have issued the following sql commands to figure out the size of my pool:
SELECT name, value FROM v$parameter WHERE name = 'sessions';
that returns back 1524.
I have also issued the sql command to see the current allocated amount:
SELECT COUNT(*) FROM v$session;
which returns back 58.
I suppose the question now is, what is causing the pooled property to go crazy.
The solution to this was to disable my pooling. I cannot tell if its a bug, r why it fails, but it does. Thankfully for me, I used jndi lookup for my dataSources, so replacing that made the spark.
dataSources:
dataSource:
pooled: false
dialect: org.hibernate.dialect.Oracle10gDialect
driverClassName: 'oracle.jdbc.OracleDriver'
username: 'superCool'
password: 'password'
url: 'jdbc:oracle:thin:#127.0.0.1:1521:coolio'
dbCreate: ''

How configure and use doctrine extension? (doctrine2-spatial)

I want use postrepgis in doctrine. I have find creof/doctrine2-spatial and I try use it. I have this error :
[Semantical Error] line 0, col 15 near 'ST_Contains(v.geopoint': Error: Class 'ST_Contains' is not defined.
I don't know why, I have read the documentation, I have try many differents configurations but alltimes same problem.
My config.yml
doctrine:
dbal:
driver: pdo_pgsql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
types:
geometry: CrEOF\Spatial\DBAL\Types\GeometryType
point: CrEOF\Spatial\DBAL\Types\Geometry\PointType
polygon: CrEOF\Spatial\DBAL\Types\Geometry\PolygonType
linestring: CrEOF\Spatial\DBAL\Types\Geometry\LineStringType
mapping_types:
_text: string
orm:
auto_generate_proxy_classes: "%kernel.debug%"
#naming_strategy: doctrine.orm.naming_strategy.underscore
entity_managers:
default:
dql:
numeric_functions:
ST_Contains: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\STContains
Contains: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\STContains
st_area: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\STArea
st_geomfromtext: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\GeomFromText
st_intersects: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\STIntersects
st_buffer: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\STBuffer
point: CrEOF\Spatial\ORM\Query\AST\Functions\PostgreSql\STPoint
auto_mapping: true
And my repository
public function findPointIn(){
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder = $queryBuilder->select('v')
->where(
$queryBuilder->expr()->eq(
sprintf("ST_Contains(v.geopoint , v.geopoint)"),
$queryBuilder->expr()->literal(true)
)
);
return $queryBuilder->getQuery()->getResult();
}
According to documentation, your numeric_functions names in config.yml must be lowercase. Change to:
numeric_functions:
st_contains: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\STContains
contains: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\Contains
st_area: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\Area
st_geomfromtext: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\GeomFromText
st_intersects: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\STIntersects
st_buffer: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\STBuffer
point: CrEOF\Spatial\ORM\Query\AST\Functions\MySql\Point

Resources