Using full java object package paths inline in autogenerated code - jooq

We have a pretty comic situation: a Postgres DB with a schema called Internal:
public class Internal extends SchemaImpl
Now we have to create an enum, with one of the values being called... Internal. The autogenerated code for this enum doesn't compile due to the collision between the enum name and the schema name, returned by getSchema() method:
///
import blabla.jooq.internal.Internal;
///
#Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
#SuppressWarnings({ "all", "unchecked", "rawtypes" })
public enum TypeEnum implements EnumType {
Internal("Internal"), External("External");
/////
/**
* {#inheritDoc}
*/
#Override
public Schema getSchema() {
return Internal.INTERNAL; //<<< The compiler thinks Internal is the enum value and not the schema name static object and fails
}
The two options to fix this are:
Rename the enum (which for some reason we would like to avoid)
Make the autogenerated code have the package names being included with the object references inline
Is there any configuration that will let us achieve option 2?
TIA

Bug in the code generator
That looks like a bug in jOOQ's code generator. I've created: https://github.com/jOOQ/jOOQ/issues/13692
Work around by renaming generated objects
You can work around such bugs by using a "generator strategy":
Programmatic: https://www.jooq.org/doc/latest/manual/code-generation/codegen-generatorstrategy/
Configurative: https://www.jooq.org/doc/latest/manual/code-generation/codegen-matcherstrategy/
With such a strategy, you can rename either the schema or enum class name, or both, of course, depending on your tastes:
<configuration>
<generator>
<strategy>
<matchers>
<schemas>
<schema>
<expression>INTERNAL</expression>
<schemaClass>
<transform>PASCAL</transform>
<expression>$0_SCHEMA</expression>
</schemaClass>
</schema>
</schemas>
<enums>
<enum>
<expression>INTERNAL</expression>
<enumClass>
<transform>PASCAL</transform>
<expression>$0_ENUM</expression>
</enumClass>
</enum>
</enums>
</matchers>
</strategy>
</generator>
</configuration>
Work around by avoiding imports for certain types
Alternatively, you can specify which type references should always remain fully qualified, rather than imported, see:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-advanced/codegen-config-generate/codegen-generate-fully-qualified-types/
For example:
<configuration>
<generator>
<generate>
<fullyQualifiedTypes>.*\.INTERNAL</fullyQualifiedTypes>
</generate>
</generator>
</configuration>

Related

Jooq XML Database generation

I am manually defining a Database XML schema to use the Jooq capabilities to generate the corresponding code from the definition.
I am using Gradle to generate the code with Jooq:
jooq {
version = '3.13.5'
edition = nu.studer.gradle.jooq.JooqEdition.OSS
configurations {
crate {
generationTool {
logging = org.jooq.meta.jaxb.Logging.INFO
generator {
database {
name = 'org.jooq.meta.xml.XMLDatabase'
properties {
property {
key = 'dialect'
value = 'POSTGRES'
}
property {
key = 'xmlFile'
value = 'src/main/resources/crate_information_schema.xml'
}
}
}
target {
packageName = 'it.fox.crate'
directory = 'src/generated/crate'
}
strategy.name = "it.fox.generator.CrateGenerationStrategy"
}
}
}
}
}
and this is the XML file crate_information_schema.xml I am referencing:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<information_schema xmlns="http://www.jooq.org/xsd/jooq-meta-3.14.0.xsd">
<schemata>
<schema>
<catalog_name></catalog_name>
<schema_name>doc</schema_name>
<comment></comment>
</schema>
</schemata>
<tables>
<table>
<table_catalog></table_catalog>
<table_schema>doc</table_schema>
<table_name>events</table_name>
<table_type>BASE TABLE</table_type>
<comment></comment>
</table>
</tables>
<columns>
<column>
<table_catalog></table_catalog>
<table_schema>doc</table_schema>
<table_name>events</table_name>
<column_name>data_block['angularPositionArray']</column_name>
<data_type>real_array</data_type>
<character_maximum_length>0</character_maximum_length>
<numeric_precision>19</numeric_precision>
<numeric_scale>0</numeric_scale>
<ordinal_position>1</ordinal_position>
<is_nullable>false</is_nullable>
<comment>angularPositionArray</comment>
</column>
<column>
<table_catalog></table_catalog>
<table_schema>doc</table_schema>
<table_name>events</table_name>
<column_name>data_block['eventId']</column_name>
<data_type>bigint(20)</data_type>
<character_maximum_length>0</character_maximum_length>
<numeric_precision>19</numeric_precision>
<numeric_scale>0</numeric_scale>
<ordinal_position>1</ordinal_position>
<is_nullable>false</is_nullable>
<comment>eventId</comment>
</column>
</columns>
</information_schema>
The code generated is not good, because it indicate the Data Type used is unknown:
/**
* #deprecated Unknown data type. Please define an explicit {#link org.jooq.Binding} to specify how this type should be handled. Deprecation can be turned off using {#literal <deprecationOnUnknownTypes/>} in your code generator configuration.
*/
#java.lang.Deprecated
public final TableField<EventsRecord, Object> angularPositionArray = createField(DSL.name("data_block['angularPositionArray']"), org.jooq.impl.DefaultDataType.getDefaultDataType("\"real_array\"").nullable(false), this, "angularPositionArray");
I have a couple of questions:
which is the correct data type for Real Array?
where is the list of supported data type with the keys to use in the XML?
N.B. CrateDB is an unsupported DataBase but Jooq could talk to the DB using the Postgres driver, the only problem is to create manually the schema.
which is the correct data type for Real Array?
Use <data_type>REAL ARRAY</data_type> (with a whitespace, and upper case, see comments and issue #12611)
where is the list of supported data type with the keys to use in the XML?
It's the same as for any other code generation data source: All the types in SQLDataType are supported. The convention around array types is currently undocumented, but any of HSQLDB's or PostgreSQL's notations should work. The feature request to formally support array types as user defined types via standard SQL INFORMATION_SCHEMA.ELEMENT_TYPES is here: https://github.com/jOOQ/jOOQ/issues/8090
N.B. CrateDB is an unsupported DataBase but Jooq could talk to the DB using the Postgres driver, the only problem is to create manually the schema.
You can obviously use the XMLDatabase for this. I'm guessing you cannot use the JDBCDatabase, because the INFORMATION_SCHEMA is too different, and the PG_CATALOG schema doesn't exist? However, you could easily implement your own org.jooq.meta.Database, too, if that makes more sense.

marshal JAXB generated classes without XmlRootElement with Apache camel

In order to marshal jaxb classes with Apache Camel the jaxb class needs to include a XmlRootElement annotation.
When generating jaxb classes from XSD the XmlRootElement annotation might not be generated.
This will lead to an Exception during marshalling
"No type converter available to convert from type: "
As soon as I add the #XmlRootElement manually, everything works fine, but since these Jaxb classes are generated, adding the anntotation manually is no option.
According to the Camel documentation in such a case, the JaxbDataFormat can be set to 'fragement(true)
JaxbDataFormat jaxbMarshal = new JaxbDataFormat();
jaxbMarshal.setContextPath(ObjectFactory.class.getPackage().getName());
jaxbMarshal.setFragment(true);
Unfortunately I still get the same exception.
Is there a way to configure JaxbDataFormat different, i.e. to define the JAXBElement which is the root element, like I would do in Java
marshaller.marshal( new JAXBElement( new QName("uri","local"),
MessageType.class, messageType ));
or is there another strategy available to get the XML marshalled?
EDIT
the used route :
from("file://inbox").unmarshal(jaxbDataFormat)
.marshal(jaxbDataFormat).to("file://outbox");
the stacktrace:
java.io.IOException: org.apache.camel.NoTypeConversionAvailableException: No type converter
available to convert from type: com.xyz.AddressType to the required
type: java.io.InputStream with value com.xyz.AddressType#32317e9d at
org.apache.camel.converter.jaxb.JaxbDataFormat.marshal(JaxbDataFormat.java:148)
~[camel-jaxb-2.16.0.jar:2.16.0] at
org.apache.camel.processor.MarshalProcessor.process(MarshalProcessor.java:83)
~[camel-core-2.16.0.jar:2.16.0] at
...
[na:1.8.0_25] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25]
Caused by: org.apache.camel.NoTypeConversionAvailableException: No
type converter available to convert from type: com.xyz.AddressType to
the required type: java.io.InputStream with value
com.xyz.AddressType#32317e9d at
org.apache.camel.impl.converter.BaseTypeConverterRegistry.mandatoryConvertTo(BaseTypeConverterRegistry.java:185)
~[camel-core-2.16.0.jar:2.16.0] at
...
In Camel 2.17, the #XmlRootElement was not required. As of 2.21, it is. Unless...
The class org.apache.camel.converter.jaxb.FallBackTypeConverter changed it's implementation from:
protected <T> boolean isJaxbType(Class<T> type) {
return hasXmlRootElement(type) || JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) != null;
}
To:
protected <T> boolean isJaxbType(Class<T> type) {
if (isObjectFactory()) {
return hasXmlRootElement(type) || JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) != null;
} else {
return hasXmlRootElement(type);
}
}
By default the isObjectFactory() method returns false. If you set the property CamelJaxbObjectFactoryon your CamelContext to true. then the JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) will return true and the deserialization works again as before without the need for an #XmlRootElement. For completeness:
<camelContext xmlns="http://camel.apache.org/schema/spring" id="camelContext">
<properties>
<property key="CamelJaxbObjectFactory" value="true"/>
</properties>
</camelContext>
I experienced the equivalent behaviour with JaxB (#XmlRootElement annotation not present in the generated class), and I suppose it comes from the way the root element is defined in the XML schema.
For example:
<xsd:element name="DiffReport" type="DiffReportType" />
<xsd:complexType name="DiffReportType">
...
</xsd:complexType>
it will generate you the DiffReportType class without the #XmlRootElement annotation. But if you directly define your root element as following, you'll get the annotation set in your generated class (the name of the root class is then DiffReport in my example).
<xsd:element name="DiffReport">
<xsd:complexType>
...
Note: I used the first way to define the complex types in my schema for class name consistency.
You can use the "partClass" option of the jaxb data format of camel. Your question is answered in the camel docs for jaxb, which describes how to marshall XML fragments (or XML generated without the XmlRootElement annotation).
Use partClass and provide the actual class name to which you wish to marshall. In case of marshalling you also have to provide the partNamespace which is the target namespace of the desired XML object.

Is there a way to ignore some entity properties when calling EdmxWriter.WriteEdmx

I am specifically using breezejs and the server code for breeze js converts the dbcontext into a form which is useable on the clientside using EdmxWriter.WriteEdmx. There are many properties which I have added JsonIgnore attributes to so that they don't get passed to the client side. However, the metadata that is generated (and passed to the clientside) from EdmxWriter.WriteEdmx still has those properties. Is there any additional attribute that I can add to those properties that I want ignored so that they are ignored by EdmxWriter.WriteEdmx? Or, would I need to make a separate method so as not to have any other unintended side effects.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
public class NorthwindMetadataContext : NorthwindContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Hide from clients
modelBuilder.Entity<Customer>().Ignore(t => t.CustomerID_OLD);
// Ignore UserSessionId in metadata (but keep it in base DbContext)
modelBuilder.Entity<Customer>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Employee>().Ignore(t => t.UserSessionId);
modelBuilder.Entity<Order>().Ignore(t => t.UserSessionId);
// ... more of the same ...
}
}
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
public class NorthwindRepository
{
public NorthwindRepository()
{
_contextProvider = new EFContextProvider<NorthwindContext>();
}
public string Metadata
{
get
{
// Returns metadata from a dedicated DbContext that is different from
// the DbContext used for other operations
// See NorthwindMetadataContext for more about the scenario behind this.
var metaContextProvider = new EFContextProvider<NorthwindMetadataContext>();
return metaContextProvider.Metadata();
}
}
public SaveResult SaveChanges(JObject saveBundle)
{
PrepareSaveGuard();
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<Category> Categories {
get { return Context.Categories; }
}
// ... more members ...
}
Pretty clever, eh?
Just remember that the UserSessionId is still on the server-side class model and could be set by a rogue client's saveChanges requests. DocCode guards against that risk in its SaveChanges validation processing.
You can sub-class your DbContext with a more restrictive variant that you use solely for metadata generation. You can continue to use your base context for persistence purposes.
The DocCode sample illustrates this technique with its NorthwindMetadataContext which hides the UserSessionId property from the metadata.
It's just a few extra lines of code that do the trick.
The Web API controller delegates to the NorthwindRepository where you'll see that the Metadata property gets metadata from the NorthwindMetadataContext while the other repository members reference an EFContextProvider for the full NorthwindContext.
Pretty clever, eh?
If you use the [NotMapped] attribute on a property, then it should be ignored by the EDMX process.

ektorp / CouchDB mix HashMap and Annotations

In jcouchdb I used to extend BaseDocument and then, in a transparent manner, mix Annotations and not declared fields.
Example:
import org.jcouchdb.document.BaseDocument;
public class SiteDocument extends BaseDocument {
private String site;
#org.svenson.JSONProperty(value = "site", ignoreIfNull = true)
public String getSite() {
return site;
}
public void setSite(String name) {
site = name;
}
}
and then use it:
// Create a SiteDocument
SiteDocument site2 = new SiteDocument();
site2.setProperty("site", "http://www.starckoverflow.com/index.html");
// Set value using setSite
site2.setSite("www.stackoverflow.com");
// and using setProperty
site2.setProperty("description", "Questions & Answers");
db.createOrUpdateDocument(site2);
Where I use both a document field (site) that is defined via annotation and a property field (description) not defined, both get serialized when I save document.
This is convenient for me since I can work with semi-structured documents.
When I try to do the same with Ektorp I have documents using annotations and Documents using HashMap BUT I couldn't find an easy way of getting the mix of both (I've tried using my own serializers but this seems to much work for something that I get for free in jcouchdb). Also tried to annotate a HashMap field but then is serialized as an object and I get the fields automatically saved BUT inside an object with the name of the HashMap field.
Is it possible to do (easily/for free) using Ektorp?
It is definitely possible. You have two options:
Base your class on org.ektorp.support.OpenCouchDbDocument
Annotate the you class with #JsonAnySetter and #JsonAnyGetter. Red more here: http://wiki.fasterxml.com/JacksonFeatureAnyGetter

XmlSerializer, XmlArray with dynamic content... how?

To start: This is also for REST deserialiaztion, so a custom XmlSerializer is out of the question.
I have a hjierarchy of classes that need to be serializable and deserializable from an "Envelope". It has an arrayelement named "Items" that can contain subclasses of the abstract "Item".
[XmlArray("Items")]
public Item [] Items { get; set; }
Now I need to add XmlArrayItem, but the number is not "fixed". We use so far reflection to find all subclasses with a KnownTypeProvider so it is easy to extend the assembly with new subtypes. I dont really want to hardcode all items here.
The class is defined accordingly:
[XmlRoot]
[KnownType("GetKnownTypes")]
public class Envelope {
but it does not help.
Changing Items to:
[XmlArray("Items")]
[XmlArrayItem(typeof(Item))]
public Item [] Items { get; set; }
results in:
{"The type
xxx.Adjustment
was not expected. Use the XmlInclude
or SoapInclude attribute to specify
types that are not known statically."}
when tyrying to serialize.
Anyone an idea how I can use XmlInclude to point to a known type provider?
The KnownTypesAttribute does not work for XmlSerializer. It's only used by a DataContractSerializer. I'm quite sure that you can exchange the serializer in WCF, because I have done that for the DataContractSerializer. But if that's not an option, you have to implement IXmlSerializable yourself and handle type lookup there.
Before disqualifying this solution: You just have to implement IXmlSerializable just for a special class which replaces Item[]. Everything else can be handled by the default serializer.
According to: http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/83181d16-a048-44e5-b675-a0e8ef82f5b7/
you can use different XmlSerializer constructor:
new XmlSerializer(typeof(Base), new Type[] { typeof(Derived1), ..});
Instead of enumerating all derived classes in the base definition like this:
[System.Xml.Serialization.XmlInclude(typeof(Derived1))]
[System.Xml.Serialization.XmlInclude(typeof(Derived2))]
[System.Xml.Serialization.XmlInclude(typeof(DerivedN))]
I think you should be able to use your KnownTypeProvider to fill the array in the XmlSerializer's constructor.

Resources