I would like to have a table with a string column as a primary key without having to use raw SQL syntax.
Here's my fluent "preparation":
static func prepare(_ database: Database) throws {
try database.create("roles") { roles in
roles.id("name")
roles.string("readable_name")
}
}
According to both my tests and the docs, resulting query will be similar to:
CREATE TABLE `roles` (`name` INTEGER PRIMARY KEY NOT NULL, `readable_name` TEXT NOT NULL)
I could not, so far, find a way to have a string (TEXT, VARCHAR, ...) as a primary key without raw SQL syntax and i would like to know whether it's possible to do it or not using the fluent query builder which comes with vapor.
Support for ID types besides INT was added in Fluent 2.
https://docs.vapor.codes/2.0/fluent/model/#id-type
Related
I have recently been using PostgreSQL rather than SQL, so finding a lot of little nuances between the two.
I want to be able to make a string value unique in a table, so using the EF code first fluent API, I have this code.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyTable>()
.HasIndex(u => u.UniqueValue)
.IsUnique();
base.OnModelCreating(modelBuilder);
}
When creating a migration, it will generate this.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_MyTable_UniqueValue",
table: "MyTable",
column: "UniqueValue",
unique: true);
}
This will then add the index to the PostgreSQL table, and work when the word is of the same case.
e.g. Try and insert "Hello" twice, and it will not work.
It does allow for variations of the word though, so I can insert "Hello", "HEllo", "HELlo", etc...
It looks like it is possible to force the case on the index in PostgreSQL using something like
CREATE UNIQUE INDEX UniqueValue ON MyTable (UPPER(UniqueValue));
However I am not sure how to do this via EF Fluent API and create the migration from that?
It seems like for now you'll have to set up a raw SQL migration. Support for that still hasn't been added (yet). You could also set up a generated (computed) column that holds the result of upper(UniqueValue), then add a unique index on that.
There's no way to add an expression-based unique constraint or to add the expression to an existing unique index key. You can build a new index using your definition as is:
CREATE UNIQUE INDEX UniqueValue ON MyTable (UPPER(UniqueValue));
Or add an exclusion constraint which ends up doing pretty much the same (as of now, quite far from being supported in EF):
create table test(txt text);
alter table test
add constraint case_insensitive_unique
exclude using gist(upper(txt) with =);
insert into test(txt) select 'hello';
--INSERT 0 1
insert into test(txt) select 'Hello';
--ERROR: conflicting key value violates exclusion constraint "case_insensitive_unique"
--DETAIL: Key (upper(txt))=(HELLO) conflicts with existing key (upper(txt))=(HELLO).
Demo
I know there are dozens of tutorials for how to do this across just as many websites, but this is my first time trying to connect a database table to a UI, so when the version of Spring Boot/MyBatis/Vaadin, for example, are different than the one I'm working with, or they use JPA or JDBC instead of MyBatis, I have no idea how to change it to work with my specific situation.
When people say "it's no different than any other method of doing it with " that doesn't help AT ALL, since, as I stated earlier, I've never done it before. Annotations and classes in the code examples of a tutorial get removed and deprecated with every new version with no clear explanation of how to change it to work with the newer version. I've been researching the various APIs (Spring Boot, Vaadin, MyBatis) for about a month and have a vague understanding of what each one does but not how they work together to achieve the desired result of making a UI for a database. I'm just getting really frustrated at how a single deprecated annotation or class in a tutorial can bring the whole thing crashing down. I know that was long-winded but I just wanted you all to understand where I'm coming from. I'm not particularly attached to any single API, just whatever is easiest.
My current dependencies are:
- Maven : 4.0.0
- Spring Boot: 2.1.2.RELEASE
- Vaadin: 12.0.4
- MyBatis Spring Boot Starter: 2.0.0
I got the starter package from Spring Initializr and added the MyBatis dependency later.
I have a PostgreSQL 10.5 database with 17 tables that will eventually be a UI for a store manager to use for things like looking at received inventory shipments, the hours an employee worked, and other tasks.
My database is named 'store', user: 'store', password: 'store' (if it matters).
For example, these are a few of my tables:
CREATE TABLE IF NOT EXISTS supplier (
id SERIAL,
brand VARCHAR(30) NOT NULL,
phone VARCHAR(15) NOT NULL,
address VARCHAR(100) NOT NULL,
CONSTRAINT pk_supplier PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS shipment (
id SERIAL,
shipdate DATE NOT NULL,
shiptime TIME NOT NULL,
status VARCHAR(10) DEFAULT 'arrived' NOT NULL,
sid INT NOT NULL,
CONSTRAINT pk_shipment PRIMARY KEY (id),
CONSTRAINT fk_shipment_supplier FOREIGN KEY (sid)
REFERENCES supplier(id)
);
CREATE TABLE IF NOT EXISTS shipmentcontains (
shipid INT NOT NULL,
iid INT NOT NULL,
quantity INT NOT NULL,
price DEC(6,2) NOT NULL,
CONSTRAINT pk_shipmentcontains PRIMARY KEY (shipid, iid),
CONSTRAINT fk_shipmentcontains_shipment FOREIGN KEY (shipid)
REFERENCES shipment(id),
CONSTRAINT fk_shipmentcontains_item FOREIGN KEY (iid)
REFERENCES item(id)
);
CREATE TABLE IF NOT EXISTS item (
id SERIAL,
itemtype VARCHAR(25) NOT NULL,
itemsize VARCHAR(10) NOT NULL,
price DEC(5,2) NOT NULL,
sid INT NOT NULL,
CONSTRAINT pk_item PRIMARY KEY (id),
CONSTRAINT fk_item_supplier FOREIGN KEY (sid)
REFERENCES supplier(id)
);
CREATE TABLE IF NOT EXISTS employee (
id SERIAL,
lastname VARCHAR(40) NOT NULL,
firstname VARCHAR(40) NOT NULL,
hourlywage DEC(4,2),
manager BOOLEAN DEFAULT false NOT NULL,
CONSTRAINT pk_employee PRIMARY KEY (id)
);
If someone can give me a code example of how to just get one of those to show in a Grid, I'm sure I can figure out how to do the rest of it. I have the connection details in my application.properties file, but I've seen that with newer versions of MyBatis this isn't needed and annotations such as #Update can be used on the SQL statements to replace that. Also, in plain English, what the heck is a Spring Bean? I hope that wasn't too long..or not long enough.
EDIT: Current version of Vaadin 12 is 12.0.4
You are asking quite a lot, so I will try to touch everything a little and nothing too detailed. I hope this helps you getting the ball rolling.
First off, you will need a java class with all fields that you have in the supplier table, annotated with #Entity. The #Table annotation lets you define the Db table name, and it is not necessary if the table is called the same as the class (case insensitive):
#Entity // javax.persistence
#Table(name = "supplier") // javax.persistence
public class Supplier {
#Id // javax.persistence
private Long id;
private String brand;
private String phone;
private String address;
public Supplier(){
}
// public getters and setters for all fields
// omitted for brevity
}
Now that you have a class for your table, you can start with creating a Vaadin Grid for it. This can be done the easiest with Grid<Supplier> supplierGrid = new Grid<Supplier>(Supplier.class);.
Now to fill the grid with items (suppliers). This is done with supplierGrid.setItems(allSuppliers);. But where do allSuppliers come from you ask?
They can be fetched using a Repository. Because the repository will be annotated with #Repository, its a spring component that can be automatically generated by spring and can be Injected/Autowired (i.e. in your view) using #Inject/#Autowired.
Then you simply call List<Supplier> allSuppliers = supplierRepository.findAll() and you have a list of all suppliers from your DB, that you now can put into the grid with the aforementioned supplierGrid.setItems(allSuppliers);
Any class where an instance of it can be injected by spring is a spring-bean, this includes classes annotated with either #Component, #Serivce or #Repository. Entities like Supplier can not automatically be injected by Spring, unless you define this is your #Configuration class:
/* Do this only if you want to inject a Supplier somewhere. */
#Bean
public Supplier supplier(){
/* define here how a default Supplier should look like */
return new Supplier();
}
I have two tables Employee and Address having one-to-one relationship.
CREATE TABLE EMPLOYEE(
ID BIGINT PRIMARY KEY NOT NULL,
EMP_NAME VARCHAR(50) NOT NULL,
PHONE_ID BIGINT,
DELETED BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT CONSTRAINT1 FOREIGN KEY (PHONE_ID)
REFERENCES PHONE (ID)
)
CREATE TABLE PHONE(
ID BIGINT PRIMARY KEY NOT NULL,
PH_NUMBER VARCHAR(20) NOT NULL,
DELETED BOOLEAN NOT NULL DEFAULT FALSE,
)
I am using Spring Data REST.
Q1. I want to expose a single data rest repository method to update DELETED column for both EMPLOYEE and `PHONE.
Something like below:
TestRepository implements CrudRepository{
#Query(value="update both table query", native=false)
public void updateBoth();
}
Q2. Is doing so even possible using Spring data REST.
PLEASE NOTE: I do not want to use native query, i.e. #Query(value="", native="true")
You have to find the balance between using the framework properly and overusing it.
Spring Data REST is to expose your repositories to HTTP but you can't solve everything with it.
The proper way here is to create a custom Controller and implement the functionality you want with proper transaction management to have the data integrity you need.
Using fuelphp, is there a way to modify an existing \ORM\Model to convert it to \ORM\Model_Temporal? I started looking into creating a migration, which needs to 1) add the necessary columns to my table (not difficult); 2) update the primary key to include the new columns as part of a compound primary key (difficult). The problem I am having is that I don't see a method in the DBUtil that performs this operation.
What is the right way to do this?
Currently DBUtil does not include a way to alter primary keys because the method of doing so differs between DB systems and DBUtil is not actually that aware what DBMS you are using.
To do this you will have to construct the query manually in your migration and use DB::query() to execute it.
In case anyone else is faced with the same task, here is what I ended up doing. My database is MySQL, the syntax may have to be changed if you're using a different DBMS. My original primary key was an auto-incrementing column named id.
class Modify_TableName_To_Temporal
{
public function up()
{
\DBUtil::add_fields('tablename',array(
'temporal_start'=>array('constraint'=>11, 'type'=>'int','default'=>0),
'temporal_end'=>array('constraint'=>11, 'type'=>'int','default'=>2147483647),
));
\DB::query('ALTER TABLE tablename MODIFY id int, DROP PRIMARY KEY')->execute();
\DB::query('ALTER TABLE tablename ADD PRIMARY KEY (id, temporal_start, temporal_end), MODIFY id int auto_increment')->execute();
}
public function down()
{
\DB::query('ALTER TABLE tablename MODIFY id int, DROP CONSTRAINT primary')->execute();
\DB::query('ALTER TABLE tablename ADD PRIMARY KEY (id), MODIFY id int auto_increment')->execute();
\DBUtil::drop_fields('tablename',array('temporal_start','temporal_end'));
}
}
Is there a way to have entity framework use a SQL default value on an insert and yet allow updating to the field. We have an instance where a SQL table has an identity column "id" and another column which is set to ident_current("table"). The only way that I know of to get the field inserted with the default value is to set the field as DatabaseGenerated(DatabaseGeneratedOption.Computed) so that it is ignored on the insert. However by having that attribute then we cannot perform an update to the column. Also it's a self referencing foreign key so we can't do an insert then immediate update to get around the issue. Don't ask me why the table is designed this way - just the way it was set up before so we're kind of stuck with it for now. A simple diagram of our setup is below:
DomainClass:
Class1 {
public int id {get;set;}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int id2 {get;set;}
public string Name {get;set;}
}
SQL (pseudo):
Table (
id INT which is an identity(1,1) column,
id2 INT NOT NULL with a default value of ident_current("table")
Name nvarchar(50)
)
We would want the insert statement generated by EF to be:
INSERT INTO Table(Name) VALUES('Name')
and the update to be:
UPDATE table
SET id2 = *somenumber*, name = 'Name'
Thanks a lot for all the help. We are using EF 4.3.1.0 if that's needed as well.
There is no way AFAIK. See this and that.
The first link points to a suggestion about using sequences as primary keys, which seems like something you might want to do instead given your example code.
The second link points to a suggestion about generic handling of default values, which is currently not supported either, but would be another potential starting point toward adding support for what you need.