OGh APEX World 2015

DSC_9284-001 (800x533)

iAdvise Diamond Sponser APEX World 2015

After 5 successful earlier editions of the APEX World day, they chose a different venue for the 2015 edition. The ship SS Rotterdam was the setting for the annual APEX World event. For us, it was not only the location that was special, we were also official Diamond Sponsor of APEX World 2015! And a great day it was! Not only to meet a lot of nice people from the Benelux APEX community, but also for the great sessions that have provided us with a lot of inspiration for the upcoming period. It was really an honour for us to be the Diamond Sponsor this year.

 

Like previous years, the day started with a short opening. This year, Reinier van Grieken, vice president and managing director of Oracle Netherlands was invited to do the opening. He emphasized that the OGh did a great job by welcoming such an enormous audience, which continued to grow over the past 5 years (more than 400 visitors this year).

In this overview we will take you aboard the SS Rotterdam on the 25th of March, so if you missed it, you’ll still get the highlights. And if you were there, you can look back on a great day.

 

Keynote: OraEvents – Stijn van Raes (iAdvise) & Yves van Looy (Oracle Belgium)

Stijn van Raes at APEX World 2015

Stijn van Raes at APEX World 2015

In an entertaining presentation the speakers told us about how Oracle and iAdvise created the situation of a supplier – customer – supplier – customer. iAdvise was hired to build the OraEvents application for Oracle (www.oraevents.eu). The architecture of this application makes it possible to collect data about the interests of registered users. In some situations this can be a huge advantage. For example, there was a situation when Oracle organized an event but it was really hard to get enough attendees. Oracle Belgium searched in the database of OraEvents and in half an hour they filled up the event.

At the end of this keynote, iAdvise wondered what could be possible features in future APEX versions (like  APEX 6, APEX 7 or even later). Thoughts about single page web applications, Node.js integration and Source Control were brought to attention. iAdvise hopes that the APEX community will think about these possible features and bring over these ideas on  https://apex.oracle.com/vote or the newly formed linkedin group http://goo.gl/L9DirJ

 

APEX suitable for high volume environments – PostNL & Fiton

Fiton and PostNL developed an application to process postal orders in a very easy and quick manner. Not only national but also international orders can be handled with this system.

Basic and simple APEX functionality was used to enter barcodes in a system which looked like some kind of wizard. Only 4 APEX pages were developed, besides a login system. The application was based on Theme 13 in APEX. The part of the system that takes care of printing invoices was built as a Java applet. The philosophy of this client case was: “Less is more”. Keep your application as simple as possible.

 

Oracle Forms to Oracle Application Express migration – iAdvise (Yvonne van Dorst) and Robeco (William de Vries)

Robeco decided to migrate 7 applications which had been developed with Oracle Designer. These applications have to be compatible with at least an Oracle 11g database and will need to have a distinct corporate identity. APEX turned out to be the best tool. In the beginning Robeco and iAdvise started to migrate 3 applications with the intention of migrating 4 more applications in the future. For Robeco it was very important to use one template for all applications. This could be realized by an APEX theme developed by iAdvise based on bootstrap in combination with a JavaScript framework.
All 7 APEX applications were developed in a period of 3 months. The business has become APEX minded and almost all questions are more or less like: “Can we do this in APEX?” The answer in most cases is: “Yes we can!” Robeco is a very satisfied APEX customer.

 

Design of good-looking, flexible and responsive applications – iAdvise (Menno Hoogendijk) and Twinq (Laurens Hamelink)

Twinq develops software for co-operations of owners of apartments. People who use this software will do this on several devices like a desktop (24 inch), a tablet (10 inch) or smartphone (5 inch). Twinq’s vision is that software has to be sexy and good-looking. To make this kind of software for all types of devices, iAdvise and Twinq used bootstrap. The APEX application uses AJAX and javascript, but all in favor of usability. Besides this, the applications are equipped with the possibility to use keyboard shortcuts.

At the end of this presentation, the audience was asked to visit an online vote, just like tinder. They could indicate whether they really experienced the demo as “Sexy Software” or not. More than 80% was convinced of the sexiness of the software.

 

Hidden Nuggets in Application Express 5.0 Page Designer – David Peake (Oracle US)

Everyone knows that APEX 5.0 has a lot in store for us. In this presentation, David Peake took
us through the new page designer and told us his favorite features.

The page designer is now divided in three sections. The old tree view can be consulted on the left side of the screen.

The middle section consists of a wide grid layout where we can easily drag and drop new items to our page. Messages will show you what is missing for the new created items.

In the right section we can directly see and edit the properties of the selected item. This new interface allows us to develop much more efficiently. Besides editing one item at a time, it is now possible to edit multiple items. This will save us a lot of time, for example when you want to change the template of all your labels. And when the latest adjustment to your page does not meet your expectations, you can now undo and redo it by simply pressing a button.

Not only the grid layout is quite convenient, keyboard shortcuts will soon be our best developing buddies. To help us along the way, help texts and search capabilities have been increased considerably. Now we will lose less time, if we are looking for that one particular item.

The page designer will give us the possibility to develop even faster!

 

Interactive Reports: Watch out for the Pitfalls! – Peter Raganitsch (FOEX (AT))

In APEX, we’ve got two different report types to work with: Classic and interactive reports. Many developers prefer the interactive report, because the user can change the appearance of the data through several manipulations. This means that the query behind this report is also adjusted. To figure out how the query will look like after a few filters, you can use the debug function. To get the full query available in logging, you must replace the ‘YES’ with ‘LEVEL9’ in the url. This gives you a more detailed logging. You can also add a small PL/SQL code in the application properties (Security, Database Session, Initialization PL/SQL code):
“APEX_DEBUG.enable (9) ;”. Remember after testing to remove it again.

The pagination type also affects the query of your report. If you use the type ‘X to Y of Z’, then the query will be extended to return the total number of rows. Therefore, use the type ‘X to Y’ for reports with many records.

Also, there is a very useful package for interactive reports named APEX_IR. This can be used to see the full runtime query of your interactive reports, add filters, clear or reset the report settings, delete saved reports and manage subscriptions.

Version 5.0 has several enhancements on interactive reports. Henceforth, it will be possible to use multiple interactive reports per page. Besides that, the action region – after selecting an option in the action menu – will be showed in modal dialogs instead of above the report. The menu is also expanded with the pivot function and the end user can now do a group by on a maximum of eight fields instead of five. With all these improvements, version 5.0 will be very well received by both, developer and end user.

 

A Spotlight on the Smaller Features of APEX 5 – Dietmar Aust (Opal Consulting (DE))

After three early adopters and a pre-production version of APEX 5, everyone nowadays knows about the most important and biggest new features of APEX 5. But what about those features you don’t notice immediately? Dietmar Aust took us through some of those features during his session about the smaller features of APEX 5.

One of the functional improvements Dietmar told us about is: the file storage. In the past apex versions there were no relative file paths, you had to upload your files individually and updating your files took a lot of time. In the new file storage you do have relative file paths and there is the possibility to upload zip files that will be extracted into the correct folders. Updating your files can be done this way too. Also after updating your files you’ll notice at runtime that the version number, which you can find in the url referencing your files has changed to avoid caching issues.

Another runtime engine improvement is the extended substitution syntax where you can now define different escaping modes. For example: if you use a dynamic action that creates a JavaScript alert to show an application item like ‘&P1_ITEM.’. In this case, cross site scripting can be applied. To avoid this there is now the possibility to escape JS syntax by defining the application item like this: ‘&P1_ITEM!JS.’.  There are a few more escaping options that can be used. These can be found in the slides that you can download from Dietmar Aust’s blog (http://daust.blogspot.be/2015/03/slides-from-my-presentation-at-apex.html) like all other improvements that have been discussed in the session.

 

Keynote: Elegance in the Enterprise – Shakeeb Rahman (Oracle US)

On every project I’ll try to surpass my previous creations in usability and design. And every time I’ll spend some time analyzing the most popular websites about their latest user interfaces and the way they interact with the end user. How do they present their navigation? Where do they put the buttons? And how do they organize their content on a page? All these questions are important because you want your users to feel comfortable when they use your application.

This keynote felt like a spring breeze on the – maybe a little bit – too hot SS Rotterdam. It was refreshing to see the amount of dedication, consideration and research the APEX design development team put in their Universal Theme. Shakeeb Rahman brought a lot of liberating facts about the way users interact with web applications to our attention. One of the statements that caught my attention was:

Most users put a higher rate on an application after the design is improved because they believe it works better than the old one.

Shakeeb also told us to think twice before using reports. Because in some cases it may not be the best approach for presenting the data to the end user. Sometimes it’s more efficient to use a diagram or a chart because users instantly see how your data it related. And if using a report still is the best approach, try to eliminate the columns users don’t really need to see right away. Minimizing the amount of data on a screen results in faster reading and, in that way, is more efficient.

Another thing to reflect on when building your application, is to split the contents into smaller bits by grouping similar fields. By dividing your contents, users don’t see your page as a big chunk of data,  but more like smalls pieces of useful information.

The new built-in template that comes with APEX 5 results in easy-to-create, smart, responsive web applications because APEX has done the research for you. It uses built-in crisp icons and follows the currently hot “flat design trend”. Now it’s not only faster to create user friendly web application it’s also much easier because they put all the know-how about user interaction in the new and improved application builder.

That’s also handy because now you can take a peek at how APEX pages are designed if you’re looking for inspiration instead of googling around the web like I used to do before.

iAdvise consultant at APEX World 2015

iAdvise consultant at APEX World 2015

Java 8 and Spring 4

On March 27, 2014, the Spring community was happy to announce the Spring Framework 4.0.3. The reason why is that it was the first release of the Spring Framework with Java 8 support. Since then, the Spring team worked very hard to provide major updates for the Spring Framework to be even more supportive towards Java 8. As of September 4, 2014, the Spring Framework 4.1 has arrived with even more support towards the Java community.

Lambda support in Spring 4.0

A good example to show the Lambda support in Spring is the JdbcTemplate class. If we would like to obtain female employees and map them as an instance of type Employee, we could do something like this:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.query("SELECT first_name, last_name FROM employee WHERE gender = ?",
                   ps -> {ps.setString(1, "FEMALE");},
                   (rs) -> new Employee(rs.getString(1), rs.getString(2)));

The complete list of Spring 4.0 features can be found here.

Optional for injection points and MVC handler parameters in Spring 4.1

As of Spring 4.1, JDK 1.8’s java.util.Optional is now supported for @RequestParam, @RequestHeader, and @MatrixVariable controller method arguments. This means that while using Java’s Optional, you make sure that parameters are never null.

The following example shows an example of a @RequestParam in Spring MVC prior to version 4.1.

@RequestMapping(value="/iadvise")
public String findEmployee(@RequestParam(value = "employeeId") String employeeId) {
  StringBuilder response = new StringBuilder("id: ");
  response.append(employeeId.toUpperCase());
  return response.toString();
}

When calling the findEmployee method without providing an employeeId, the method will return a NullPointerException. This can be solved by Java’s Optional element.

@RequestMapping(value="/iadvise")
public String findEmployee(
  @RequestParam(value = "employeeId") Optional<String> employeeId){
  StringBuilder response = new StringBuilder("id: ");
  employeeId.ifPresent(value -> response.append(employeeId.toUpperCase()));
  return response.toString();
}

The same result can be achieved for the @RequestHeader, and @MatrixVariable annotations.

The complete list of Spring 4.1 features can be found here.

Tableau: Delineate Belgian provinces explained

Tableau Software is a self-service BI-tool that allows data visualization, meaning that even business users should be able to easily visualise their data (without needing help of IT). You can check out the Tableau website for more information about this great tool.

As you may have already seen in several guides or tutorials, Tableau is able to link certain dimensions like postal code, countries, etc. to a certain geographical area. Unfortunately, most of these tutorials use data related to the United States of America. As of today, the Belgian geographical support is at a low point. One thing we can easily do though, is show the data based on provinces using their borders.

In this blog we’ll be showing the Belgian population per province in the year 2012 on a map. The data I used (and which you can see in the image below) can be downloaded from the Belgian Open Data website.

Data used in Excel

I’ve slightly altered the data so that we would have the English name for each province (else Tableau will not recognize it – you can also choose French or German).

I’ve opened Tableau and loaded the data. Make sure that province is set as a geographical data type (State/Province). If this is not the case you can change it by right clicking on province and then selecting “Geographical Role” -> “State/Province”.

Geographic Role - State Province

When using filled maps (=a type of map visualization that Tableau offers which will fill the area according to your chosen data), you can only use one measure. Therefore I’ve added a new calculated field “Total” (see “Analysis” -> “Create Calculated Field”) based on the male and female amount of people.

calculated field - Total population

Now we will select both the province and the total and click on “Filled maps”.

Select province and total for map2

Tableau will automatically colour the provinces according to the amount of people who live there.

Now the only thing left to do is format your layout and then you’re done! I have coloured the provinces based on the % of the total (Red -> High %, Green -> Low %). I’ve also put the % of total in the label because I think showing the normal total would be unclear. The last thing I did was add the amount of males, females, total and total percentage to the label.

end result

You can view the dashboard that I made for this blog on Tableau public. Tableau public is a free tool that Tableau offers which allows you to publish your data on the web for other people to see.

Extra tip: you can control click on multiple provinces to view the sum of the total % for the selected provinces.

Thank you for reading!

Oracle Forms 12c?

Some people wonder whether there will be a 12c version of Oracle Forms or not.

Instead of making guesses, the best way is to ask the people who know best.
So, after getting this question, I asked Michael Ferrante, Oracle Forms Product manager.

Here’s his answer:

Version 12 of Forms and Reports is tentatively planned for releases sometime in CY2015.  At this time, we (Oracle) are not permitted to share exact dates, but I can tell you that developers are hard at work with the new version.  Currently, Forms is in very early stages of its development cycle, but already contains many new features and enhancements to existing features.  This new version, of course will also offer updated certifications against newer Operating Systems, newer Java versions, newer browsers, and newer Oracle database versions.

If you want to stay updated on Oracle Forms, check the following sources on a regular base:

In case you visit Oracle Open World, check the following session:

Oracle Forms sneak peek (Moscone South, room 306 – 9/29/14, 17:15 – 18:00)

New in Java 8 : Default and static methods in interfaces

Default method’s  (aka Defender methods) in interfaces are new in Java 8. They enable you to define a default implementation of a method in the interface itself.

If an interface is implemented by several classes, it’s hard to add method’s afterwards, as it will break the code and require all implementing classes to define the method as well. Adding a method to the interface, and defining a default implementation for it, will resolve this problem.

Here’s a code example :


public Interface Draw {

public void drawCircle();

   default public void drawRectangle() {

      System.out.println("draw a rectangle");

   }

}

Implementing classes that have not defined the drawRectangle() method, will print “draw a rectangle” when drawRectangle() is executed on them.

Interfaces that extend this interface can

  • define nothing, in which case the method will be inherited
  • declare the default method again with no implementation, which will make it abstract
  • Redefine the default method so it get’s overridden

These default methods were added to Java in order to be able to implement the new Streams API. As they needed to update the Collection interface, adding the stream() and parallelStream() methods. If they didn’t had the default method, they should have updated all classes that implement the Collection interface.

Static methods

Also new in Java 8 is the use of static method’s in an Interface.

So now, drawRectangle()  could also be defined as a static method, but that would give the impression that it is a utility or helper method, and not part of the essential core interface. So in that case, it’s better to go for the default method.

You could argument that an abstract class would have done the job as well. But as Java has single inheritance, that choice would narrow down our the design possibilities. And as the poster above your bed is shouting every day : ‘Favor composition over inheritance!!’ right ? So we want to avoid inheritance anyway.

So what will happen if you try to implement 2 interfaces with the same default methods ? Well, you will get the following compile time error :

Duplicate default methods named [methodname] with the parameters () and () are inherited from the types [interface1] and [interface2]

To avoid this error, choose an implementation of one of the interfaces :

interface Draw{
   default void circle() {
     System.out.println("draw circle");
   }
}
interface Print{
   default void circle() {
     System.out.println("print circle");
   }
}

class MyClass implements Draw, Print {
   @Override
   public void circle() {
     Draw.super.circle();
   }
}

That’s it, a quick overview of this new feature in Java 8.

Caching in a JEE : don’t write it yourself, use LoadingCache from Google Guava libraries.

Caching data is something you use in almost every JEE project. Most of the time it’s pretty simple : put your data in a .properties file and use a PropertyManager to fetch the data.

But that’s not very flexible and manageable. Updating the values means, updating your property file, repackaging the ear file, and redeploying, and only developers can update the data.

Putting the data in JNDI entries, and using JNDI lookups may solve the problem of redeploying, but if you got a few 100 properties, it’s still not very manageable.

Most of the times, JNDI entries are entered via some application server console which, in a production environment, is not accessible for your users who need to manage this data.

So lets put the data that needs to be cached in a database, or make it accessible via a web service. That would be ideal. You can write your own application on it, and have the data managed by your users.

But that means that you have to write your own, thread safe, caching algorithms.

No big deal if the data only changes once every 10 years, but refreshing it on a time or size basis, makes the whole thing a bit more complicated. And that’s where the great LoadingCache class from the Google Guava library comes in.

What are the Guave libraries ? Well, here’s how they describe it : ‘The Guava project contains several of Google’s core libraries that we rely on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.’

Now for caching, the Guava LoadingCache class caches data in a key-object map, and lets you define a cache refreshing mechanism, all done in a thread safe manner.

So lets show a small  example and explain how it works. Suppose your cache contains a list of products that are on sale for 1 day. Depending on the no. of sold products, the price will increase during that day. This means that the cache should be updated every few seconds, to update the price, and after 1 day, the whole cache should be refreshed with new products. Suppose that price setting and product selections are in the database, updated by some back-end application, and we need the new data in our frontend application and we want to cache it.

All this can be done with this simple class :

import java.util.concurrent.TimeUnit;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import be.iadvise.dao.DatabaseDAO;
import be.iadvise.entities.Product;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.MoreExecutors;

@Singleton
public class ProductCache {

@EJB
 DatabaseDAO databaseDAO;
 private static final Integer REFRESH_PRODUCT_AFTER_5_SECONDS = 5;
 private static final Integer EXPIRE_PRODUCT_AFTER_1_DAY = 1;
 private final LoadingCache<String, Optional<Product>> cache;

 public ProductCache() {
      cache = CacheBuilder.newBuilder()
           .expireAfterWrite(EXPIRE_PRODUCT_AFTER_1_DAY, TimeUnit.DAYS)
           .refreshAfterWrite(REFRESH_PRODUCT_AFTER_5_SECONDS, TimeUnit.SECONDS)
           .build( new CacheLoader<String, Optional<Product>>() {
                 @Override
                 public Optional<Product> load( String productId ) throws Exception {
                     return loadCache(productId);
                 }
           }
     );
 }

 public Optional<Product> getEntry( String productId ) {
      return cache.getUnchecked( productId );
 }

 private Optional<Product> loadCache(String productId) {
      Product product = databaseDAO.getProduct(productId);
      return Optional.fromNullable(product);
 }
}

Explanation

  1. In the constructor, we build the cache using the CacheLoader, defining the refresh mechanism. In our example we define 2 rules :
    – expireAfterWrite : after this period, the object will be evicted from the cache, and replaced the next time it is requested.
    – refreshAfterWrite : after this period, the object will be refreshed using the loadCache method. (with our new price)
  2. getEntry(String productId) method : will return the object with given key. So in this example, the cache is not loaded all at once, but only when the object is needed.
  3. loadCache(String productId) : will load the product and add it to the cache, or replace it if it’s already there and needs to be refreshed.

That’s all there is to it !

A few other remarks on the code

  1. There are other mechanism like expire/refresh AfterRead, which will time only from the last read, or let the cache hold only a certain no. of objects,…
  2. This code is implemented as a session bean. To make a singleton, I’m using EJB 3 annotation @Singleton. Because I only want 1 cache in my application
  3. My DAO is also injected using the @EJB annotation
  4. The LoadingCache does not want any null objects in the map (returns an error), so I’m using the Guava ‘Optional’ class here. This is basically a wrapper for my object and used to check if there is a value for my product id or not. So if someone uses a wrong productId, my cache will indicate that there is no product for this id, and I don’t have to go to the database every time it is requested.

To conclude:

Programming a caching mechanism in a JEE environment is not as trivial as it may seem. Testing it in a multithreaded environment is even more difficult. The caching classes of Guave gives you ready-to-use solution. It’s programmed, tested and used by Google, so I think we can say in all honesty : this is proven technology.

A remark on deploying on Weblogic 12c:

Weblogic also uses the Guava libraries, but an older version. This causes following error on deployment :

java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor()

Lcom/google/common/util/concurrent/ListeningExecutorService;

Adding the following to your weblogic-application.xml will solve the problem (force weblogic to use your deployed Guava libraries :

<wls:prefer-application-packages>
<wls:package-name>
com.google.common.*
</wls:package-name>
<wls:prefer-application-packages>

Guava libraries run under Apache license, more info/download can be found on :

https://code.google.com/p/guava-libraries/

Have fun !

Another 5 neat 12c features for Oracle developers

In this post I will put 5 other new 12c features in the spotlight (in addition to the features of a previous post), that really makes the 12c an improvement against the previous versions of the Oracle database.

To get this result I listed up all the major new features and wanted to know my top 5 features that would make my life easier(as a developer), excluding the features from the previous  post (I certainly would have added the sequence modification(feature 1)), when doing development on an Oracle database.

  1. Top end query -> I really like this feature and I’m still wondering why it took Oracle so long before creating it. It is something I could have used a lot in the past, but instead I had to create far too complicated, not as nice readable queries to achieve this. How does it work? Well it’s very easy and it’s readable and can be used in a wide variety of cases. Some examples:
    Only get the first 3 rows:
    select * from X order by id
    fetch first 3 rows only;

    Skip the first 3 rows and get the next 3 rows:

    select * from X order by id
    offset 3 rows fetch next 3 rows only;

    Get the first 50% of records

    select * from X order by id
    fetch first 50 percent rows only;

    Get the first 3 rows together with the records equal to these department id’s

    select * from emp order by deptno
    fetch first 3 rows with ties;

    If you want the capture the last rows, you can obviously change ‘first’ with ‘last’…

  2. In the 12c database the use of 32767 characters for a VARCHAR2 in SQL is now available instead of the maximum of 4000(this is also the case for RAW and nvarchar2).
    We all have been waiting a long time for this one and before we had to use the clob datatype.
    But beware this is not an out of the box feature, you will have to execute the lines below before this is enabled :
    shutdown immediate
    startup upgrade
    alter system set max_string_size=EXTENDED scope=both;
    @<ORACLE_HOME>/rdbms/admin/utl32k.sql
    Shutdown immediate
    Startup

    More info can be found on: http://docs.oracle.com/cd/E16655_01/server.121/e17615/refrn10321.htm

  3. The invisible column is a feature of which I was wondering where I could use it for.
    Well it could be handy when you are adding a column to your table, but you don’t want any existing code to be impacted by it.
    Another case where it could be useful, is when using audit columns.  Columns as the creation_dt, update_dt, user_creation and user_update will only be of any added value when you would like to audit a certain column.
    Packages with inserts, updates, references to this table will not be impacted by the creation of this column.
    On the other hand there is also a risk that you forget that this column is in there, because you have to explicitly call for it (a describe or select * will not show this column). You can create invisible columns like this:

    ALTER TABLE
    ADD  INVISIBLE;

    If you want to make the column visible again, use this:

    ALTER TABLE
    MODIFY VISIBLE;

    In summary it could be handy, but don’t forget this column or it will pollute your table.

  4.  The with clause inline plsql feature is also something that I think is very welcome.
    It will make it possible to create a procedure or function inside your select statement instead of having to create this in a package or function. Oracle also says that this will optimize the performance against having to call a schema procedure/function(I still have to test this).
    A little example:

    WITH
    FUNCTION fnc$_add_one(p_num IN NUMBER) IS
    BEGIN
    RETURN p_num+1;
    END;
    SELECT fnc$_add_one(1)
    FROM DUAL;
  5.  Most of the time I use the ANSI way of writing for a left outer join, but the oracle way of writing left outer joins is still often used by many of the oracle developers.
    But there was one thing that you could do in ANSI, that you couldn’t in the oracle way.  You couldn’t write multiple tables on the left of an outer join, untill12c…
    In 11g and before when coding something like this:
    select *
    from a,b,c
    where a.id = b.id
    and a.id = c.id(+)
    and b.id = c.id2(+);

    This resulted in -> ORA-01417: a table may be outer joined to at most one other table

    In 12c this will work, also the ANSI solution obviously still works both on 12c and on 11g

    select *
    from a
    JOIN b ON (a.id = b.id)
    LEFT OUTER JOIN c ON (a.id = c.id AND b.id = c.id2);

Together with the previous post this makes 10 reasons why you should start to use the Oracle 12c database :-)